2. Modelo inicial

2.1. Se plantea la siguiente primera alternativa para modelar el peso:

\(E(peso) = \beta_0 + \beta_1 * altura + \beta_2 * edad + \beta_3 * genero + \beta4 * diasActividadFisicaSemanal + \beta5 * consumoDiarioAlcohol\)

Primero se cargan las librerías necesarias:

options(warn=-1)
rm(list=ls())
gc()
          used  (Mb) gc trigger  (Mb) max used  (Mb)
Ncells 3056917 163.3    5545042 296.2  5545042 296.2
Vcells 7073314  54.0   17830652 136.1 17830652 136.1
options(warn=-2)
# install.packages("pacman") -- Descomentar par instalar pacman
library(pacman)
p_load_gh('adrianmarino/commons')
import('../src/dataset.R')
[1] "-> '../src/dataset.R' script loadded successfuly!"
import('../src/preprocessing.R')
[1] "-> '../src/preprocessing.R' script loadded successfuly!"
import('../src/model.R')
[1] "-> '../src/model.R' script loadded successfuly!"
import('../src/plot.R')
[1] "-> '../src/plot.R' script loadded successfuly!"

A continuación se carga los conjuntos de entrenamiento y test. también se resumen los valores de las variables categóricas y se excluyen las observaciones con valores faltantes, ya que son muy pocas con respecto al total.

train_set <- load_train_set() %>% 
  preprocess() %>% 
  shorten_values() %>%
  process_missings()

test_set <- load_test_set() %>% 
  preprocess() %>% 
  shorten_values() %>% 
  process_missings()
glimpse(train_set)
Rows: 7,024
Columns: 15
$ edad                          <int> 17, 15, 15, 16, 17, 15, 13, 17, 17, 16, 16, 14, 15, 17, 15, 14, 15, 17, 17, 16, 14, 12, …
$ genero                        <fct> Femenino, Masculino, Masculino, Masculino, Masculino, Masculino, Femenino, Femenino, Mas…
$ nivel_educativo               <ord> 2, 1, 2, 1, 2, 1, 9, 9, 1, 3, 3, 8, 9, 3, 9, 2, 2, 3, 3, 2, 9, 8, 2, 3, 2, 2, 3, 1, 2, 1…
$ altura                        <int> 165, 178, 172, 170, 170, 178, 156, 163, 164, 167, 185, 146, 180, 175, 183, 165, 165, 157…
$ peso                          <int> 62, 62, 62, 65, 75, 88, 46, 60, 57, 51, 100, 33, 62, 70, 80, 60, 47, 50, 50, 70, 75, 55,…
$ frecuencia_hambre_mensual     <ord> Rara vez, Rara vez, Nunca, Nunca, Rara vez, Nunca, Nunca, Nunca, Nunca, Nunca, Nunca, Ra…
$ dias_consumo_comida_rapida    <int> 0, 0, 3, 1, 1, 2, 0, 0, 0, 3, 4, 2, 1, 1, 3, 0, 0, 0, 0, 1, 0, 6, 0, 1, 0, 2, 0, 2, 0, 0…
$ edad_consumo_alcohol          <ord> 14-15, <=7, 0, 14-15, 16-17, 8-9, 10-11, 16-17, <=7, 0, 12-13, 12-13, 0, 14-15, <=7, 14-…
$ consumo_diario_alcohol        <dbl> 5.0, 4.0, 0.0, 0.0, 0.0, 5.0, 1.0, 0.5, 5.0, 0.0, 5.0, 0.0, 0.0, 2.0, 1.0, 0.0, 5.0, 0.0…
$ dias_actividad_fisica_semanal <int> 7, 7, 7, 7, 0, 7, 0, 2, 7, 3, 2, 2, 7, 1, 4, 0, 1, 6, 5, 7, 3, 0, 7, 5, 2, 2, 4, 2, 7, 4…
$ consumo_semanal_frutas        <ord> 0, 0, 0, 4-6, 14, 7, 14, 21, 0, 14, <=3, <=3, 7, <=3, <=3, <=3, 0, <=3, <=3, 14, <=3, 4-…
$ consumo_semanal_verdura       <ord> 4-6, 4-6, 7, >=28, <=3, 14, 4-6, 7, 0, 4-6, <=3, 7, 7, <=3, 4-6, <=3, <=3, <=3, 4-6, <=3…
$ consumo_semanal_gaseosas      <ord> <=3, <=3, 4-6, <=3, 7, 4-6, 0, 7, <=3, 4-6, 4-6, <=3, <=3, 4-6, 4-6, <=3, 0, 0, 0, 7, <=…
$ consumo_semanal_snacks        <ord> <=3, 0, 4-6, <=3, 0, 4-6, 0, <=3, 0, <=3, <=3, 0, <=3, 7, <=3, 0, NA, <=3, <=3, <=3, <=3…
$ consumo_semanal_comida_grasa  <ord> 0, 4-6, 0, 0, <=3, 4-6, <=3, 7, 0, <=3, 0, <=3, 0, 7, 0, 4-6, 4-6, 0, <=3, <=3, <=3, <=3…

Se fija la semilla y se validan las proporciones de los conjuntos de entrenamiento y test:

set.seed(25)
show_train_test_props(train_set, test_set)
[1] "Train: 70%, Test: 30%"

Modelo 1

Se plantea el primer modelo lineal:

model_1 <- lm(
  peso ~ altura + edad + genero + dias_actividad_fisica_semanal + consumo_diario_alcohol, 
  data = train_set
)

2.2. ¿Cuál es la interpretación de cada uno de los coeficientes estimados?

Veamos a continuación un resumen de los coeficiente del modelo 1:

coefficients_summary(model_1)
_________________________________________________________________________________________________________ 
term                               estimate  std.error   statistic       p.value    conf.low    conf.high 
========================================================================================================= 
(Intercept)                   -68.922688070 2.33805445 -29.4786497 3.614866e-180 -73.5059810 -64.33939510 
altura                          0.650606544 0.01437975  45.2446353  0.000000e+00   0.6224179   0.67879520 
edad                            1.406727060 0.09385081  14.9889709  5.121599e-50   1.2227511   1.59070300 
generoMasculino                 1.262643558 0.27282821   4.6279802  3.758831e-06   0.7278179   1.79746926 
dias_actividad_fisica_semanal  -0.087391031 0.04992917  -1.7503000  8.011025e-02  -0.1852673   0.01048523 
consumo_diario_alcohol          0.007271379 0.06138558   0.1184542  9.057112e-01  -0.1130629   0.12760566 
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ 
____________________________________________________________________________ 
Termino                         Coeficiente Signiticativo IC incluye al cero 
============================================================================ 
(Intercept)                   -68.922688070            Si                 No 
altura                          0.650606544            Si                 No 
edad                            1.406727060            Si                 No 
generoMasculino                 1.262643558            Si                 No 
dias_actividad_fisica_semanal  -0.087391031            No                 Si 
consumo_diario_alcohol          0.007271379            No                 Si 
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ 

Al analizar cada coeficiente se encuentra que:

  • \(\hat{\beta_0}\) (Ordenada al origen) de valor -68.92 Kg, es el peso esperado o promedio de un individuo de genero femenino que tiene cero altura, edad, actividad física y consumo diario de alcohol. Esto no es interpretable, ya que una persona tiene que tener una altura superior a cero y no puede tener un peso negativo, pero si podría no realizar actividad física ni consumir alcohol.

  • El coeficiente \(\hat{\beta_1}\) de valor 653 gramos, corresponde a la altura del individuo. Este coeficiente indica que dada una edad, genero, consumo de alcohol diario y días de actividad física semanal fijos, cada incremento en 1 cm adicional en la altura del individuo implica un aumento de su peso esperado o promedio de 653 gramos.

  • El coeficiente \(\hat{\beta_2}\) de valor 1.378 kg, corresponde a la edad del individuo. Este coeficiente indica que dada una altura, genero, días de actividad física y consumo de alcohol diario fijos, cada vez que el individuo cumple un año su peso esperado o promedio aumenta en 1.378 kg.

  • El coeficiente \(\hat{\beta_3}\) de valor 1.224 kg, corresponde a los individuos de genero masculinos. Este coeficiente indica que dada una altura, edad, consumo de alcohol diario y días de actividad física semanal fijos, el peso promedio o esperado para el genero masculino es 1.224 kg mayor al peso femenino (categoría basal). Por otro lado, el coeficientes no indica cunado mas alto es el peso del genero masculino respecto del femenino al fijar los demás coeficientes.

  • El coeficiente \(\hat{\beta_4}\) de valor 99.1 gramos, corresponde a los días de actividad física semanal que realiza el individuo. Este coeficiente indica que dada una altura, edad, genero y consumo de alcohol diario, cada vez que un individuo realiza un día mas de actividad física semanal su peso esperado o promedio disminuye en 99.1 gramos.

  • El coeficiente \(\hat{\beta_5}\) de valor -8 gramos, corresponde al nivel de consumo diario de alcohol del individuo. Este coeficiente indica que dada una altura, edad, genero y días de actividad física semanal fijos, cada vez que el individuo consume un trago de alcohol su peso esperado o promedio disminuye en 8 gramos. A simple vista podrá no llegar a tener sentido, ya que a mayor consumo de alcohol el peso debería aumentar, ya sea por el peso del propio liquido como el peso equivalente en grasas. Entiendo que puede tener un relación con los rangos de edades de los individuos que mas consumen alcohol (12 q 17 años), ya que estos se encuentran en pleno crecimiento.

2.3. ¿Son significativos los coeficientes?

Para determina si los coeficientes son aptos para explicar el peso de un individuo se realiza un \({T}\) test para cada coeficiente en el cual se evalúan las siguientes hipótesis:

  • \({H_0: \beta_i = 0}\)
  • \({H_1: \beta_i \neq 0}\)

Si \({\beta_i \neq 0}\) podemos decir que existe una diferencia estadisticamente significativas del cero para coeficiente \({\beta_i}\), y por lo tanto el coeficiente \({\beta_i}\) explicar la variable \({y}\) (Peso en nuestro caso).

Luego analizando la salida de coefficients_summary concluimos que:

  • Los coeficientes correspondientes al acategoria basal \({\beta_1}\)(Genero femenino), altura(\({\beta_1}\)), edad(\({\beta_2}\)) y genero masculino (\({\beta_3}\)) tienen \(p-valor < 0.05\). Por lo tanto, se rechaza la hipótesis nula y resultan estadistitamente significativos para explicar el peso. Por otro lado, se puede apreciar que los intervalos de confianza del 95% no incluyen al cero.
  • Lo contrario sucede con días de actividad física semanal(\({\beta_4}\)) y consumo de alcohol diario (\({\beta_5}\)), dado que ambos no rechazar la hipótesis nula (\(p-valor > 0.05\)) y por lo tanto no existe una diferencia significativa del cero. Finalmente, no hay evidencia estadistitamente significativas de que estos coeficientes expliquen al peso.

2.4. ¿El modelo resulta significativo para explicar el peso?

Para determinar si es modelo es significativo para explicar el peso de un individuo se realiza un \(F\) test con las siguientes hipótesis:

  • \(H_0: β_1 = β_2 = · · · = β_{p−1} = 0\)
  • \(H_1:\) Por lo menos un \(β_k\) (\(k = 1, 2,..., p−1\)) es distinto de 0.

Donde: * \(H_0\) afirma que no hay vinculo entre la variable \({y}\)(Peso) y las variables regresoras. * \(H_1\) afirma que al menos una de las variables regresoras sirve para predecir la variable \({y}\) (Peso).

Veamos los resultados el \(F\) test:

glance(model_1)

Podemos apreciar que el \(p-valor < 0.05\) e igual a 0. Con mucha certeza podemos decir que al menos una de las variables regresoras permite explicar el peso. Esto concuerda con los resultados de los \(T\) test para las los coeficientes correspondientes a altura, edad y genero femenino(basa) y masculino).

2.5. ¿Qué porcentaje de la variabilidad explica el modelo?

Según el valor de \(R^2\) ajustado (adj.r.squared), este modelo llega a explica el 35% de la variabilidad del dataset de entrenamiento, lo cual no es un valor bajo pero tampoco es despreciable.

2.6. ¿Que sucede si poner al genero masculino como variable basal?

train_set_genero <- data.frame(train_set) 
train_set_genero$genero <- factor(
  train_set_genero$genero,
  levels=c('Masculino', 'Femenino'), 
  ordered=FALSE
)
table(train_set_genero$genero)

Masculino  Femenino 
     3260      3764 
model_genero <- lm(
  peso ~ altura + edad + genero + dias_actividad_fisica_semanal + consumo_diario_alcohol, 
  data = train_set_genero
)
coefficients_summary(model_genero)
_________________________________________________________________________________________________________ 
term                               estimate  std.error   statistic       p.value    conf.low    conf.high 
========================================================================================================= 
(Intercept)                   -67.660044511 2.44965480 -27.6202364 1.682519e-159 -72.4621079 -62.85798114 
altura                          0.650606544 0.01437975  45.2446353  0.000000e+00   0.6224179   0.67879520 
edad                            1.406727060 0.09385081  14.9889709  5.121599e-50   1.2227511   1.59070300 
generoFemenino                 -1.262643558 0.27282821  -4.6279802  3.758831e-06  -1.7974693  -0.72781785 
dias_actividad_fisica_semanal  -0.087391031 0.04992917  -1.7503000  8.011025e-02  -0.1852673   0.01048523 
consumo_diario_alcohol          0.007271379 0.06138558   0.1184542  9.057112e-01  -0.1130629   0.12760566 
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ 
____________________________________________________________________________ 
Termino                         Coeficiente Signiticativo IC incluye al cero 
============================================================================ 
(Intercept)                   -67.660044511            Si                 No 
altura                          0.650606544            Si                 No 
edad                            1.406727060            Si                 No 
generoFemenino                 -1.262643558            Si                 No 
dias_actividad_fisica_semanal  -0.087391031            No                 Si 
consumo_diario_alcohol          0.007271379            No                 Si 
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ 

glance(model_genero)

Observaciones

  • En este caso, cambia el coeficiente \(\beta_0\) para la categoría basal, manteniéndose los demás coeficientes.
  • Siguen siendo significativos los mismo coeficientes. La unica diferencia es que \(\beta_0\)(basal) representa al genero masculino y \(\beta_3\) al femenino.
  • Se mantiene la significatividad global del modelo y el mismo \(R^2\).
  • Como ultimo, cabe aclarar que al cambiar la categoría basar cambian las interpretaciones del modelo.

3. Modelo categóricas

3.1. Se sugiere probar un modelo que incorpore el consumo semanal de snacks y una interacción entre el

género y la edad, en lugar de actividad física y consumo de alcohol. Además se pide explicitamente que la categoría “No comí comida salada o snacks en los últimos 7 días” de la variable consumo_semanal_snacks se encuentre como nivel/categoría basal.

\(E(peso) = \beta_0 + \beta_1 * altura + \beta_2 * edad + \beta_3 * genero + \beta4 * consumoSemanalSnacks + \beta_5 * genero * edad\)

Primero validamos que las primeras categorías en cada variable de tipo factor sean las correctas, ya que esta sera la que el modelo defina como categoría basal:

table(train_set$consumo_semanal_snacks)

   0  <=3  4-6    7   14   21 >=28 
2162 3144  623  604  231  100  134 
table(train_set$genero)

 Femenino Masculino 
     3764      3260 

Se puede apreciar que la primeras categorías corresponden a 0 consumo de snacks semanal y genero femenino. Por otro lado la categoría genero se encuentra balanceada.

Modelo 2

Definimos el nuevo modelo:

model_2 <- lm(
  peso ~ altura + edad + genero + consumo_semanal_snacks +  genero * edad, 
  data = train_set
)

3.2. ¿Cuál es la interpretación de los coeficientes estimados para las categorías de

consumo_semanal_snacks y genero * edad? ¿Son significativas?

coefficients_summary(model_2)
_____________________________________________________________________________________________________ 
term                         estimate  std.error    statistic       p.value     conf.low    conf.high 
===================================================================================================== 
(Intercept)              -65.56456109 2.82343748 -23.22153813 5.697145e-115 -71.09935565 -60.02976652 
altura                     0.64312289 0.01457345  44.12974931  0.000000e+00   0.61455449   0.67169128 
edad                       1.22539002 0.12134815  10.09813515  8.197651e-24   0.98751081   1.46326923 
generoMasculino           -4.60464631 2.68577421  -1.71445771  8.648904e-02  -9.86957909   0.66028646 
consumo_semanal_snacks.L  -1.20550502 0.64487036  -1.86937577  6.161235e-02  -2.46964668   0.05863664 
consumo_semanal_snacks.Q  -0.03462407 0.56980433  -0.06076485  9.515482e-01  -1.15161353   1.08236539 
consumo_semanal_snacks.C  -1.55903482 0.62845595  -2.48073841  1.313442e-02  -2.79099926  -0.32707037 
consumo_semanal_snacks^4   0.29624085 0.63955326   0.46319965  6.432357e-01  -0.95747770   1.54995939 
consumo_semanal_snacks^5   0.32794577 0.61234388   0.53555818  5.922810e-01  -0.87243411   1.52832566 
consumo_semanal_snacks^6  -0.82570831 0.50219366  -1.64420297  1.001793e-01  -1.81016033   0.15874371 
edad:generoMasculino       0.38927567 0.17949126   2.16877226  3.013360e-02   0.03741831   0.74113303 
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ 
______________________________________________________________________ 
Termino                   Coeficiente Signiticativo IC incluye al cero 
====================================================================== 
(Intercept)              -65.56456109            Si                 No 
altura                     0.64312289            Si                 No 
edad                       1.22539002            Si                 No 
generoMasculino           -4.60464631            No                 Si 
consumo_semanal_snacks.L  -1.20550502            No                 Si 
consumo_semanal_snacks.Q  -0.03462407            No                 Si 
consumo_semanal_snacks.C  -1.55903482            Si                 No 
consumo_semanal_snacks^4   0.29624085            No                 Si 
consumo_semanal_snacks^5   0.32794577            No                 Si 
consumo_semanal_snacks^6  -0.82570831            No                 Si 
edad:generoMasculino       0.38927567            Si                 No 
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ 

Si interpretamos los coeficientes que son significativos para el \(T\) test:

  1. Coeficiente correspondiente al consumo_semanal_snacks<=3:

Si fijamos los coeficientes correspondientes a la altura, edad, generoMasculino y generoMasculino*edad; el peso promedio o esperado de un individuo de consume snacks hasta 3 veces por semana es 1.43 kg menor que aquellos que no consumen snacks.

  1. Sucede algo similar con las categorias consumo_semanal_snacks4-6 y consumo_semanal_snacks>=28 donde:
  • El peso para un individio que consume de 4 a 6 veces por semana es 2.25 kg menor que aquellos con no consumen snacks.
  • El peso para un individio que consume 28 veces a mas por semana es 2.6 kg menor que aquellos con no consumen snacks.
  1. Coeficiente correspondiente al edad * genero:

Dado el modelo original:

\(E(peso) = \beta_0 + \beta_1 * altura + \beta_2 * edad + \beta_3 * genero + \ \beta4 * consumoSemanalSnacks + \beta_5 * genero * edad\)

y sabiendo que el genero femenino toma el valor 0 y masculino 1. Si reemplazamos estos valores en el modelo original encontramos que:

\(E_f(peso) = \beta_0 + \beta_1 * altura + \beta_2 * edad + \beta_4 * consumoSemanalSnacks\)

El genero femenino tiene la ordenada \(\beta0\) y las pendientes determinada por \(\beta_1\), \(\beta_2\) y \(\beta_4\).

\(E_m(peso) = (\beta_0 + \beta_3) + \beta_1 \* altura + (\beta_2 + \beta_2,3) * edad + \ \beta_4 * consumoSemanalSnacks\)

El genero masculino tiene una ordenada que es la suma de la ordenada del genero femenino \(\beta_0\) mas \(\beta_3\). Luego cambia la pendiente \(\beta_2\) de la edad, a la cual se le suma \(\beta_2,3\)

Luego, sabiendo que solo cambian los coeficientes correspondientes al genero y edad, si mantenemos contantes los demás coeficientes obtenemos:

  • \(E_f(peso) = \beta_0 + \beta_2 * edad + cte\)
  • \(E_m(peso) = (\beta_0 + \beta_3) + (\beta_2 + \beta_2,3) * edad\)

Ahora reemplazamos por los coeficientes por lo valores que encontró el modelo:

  • Femenino:

    • \(E_f(peso) = -65.56456109 + 1.22539002 edad + cte\)
  • Masculino:

    1. \(E_m(peso) = (-65.56456109 + -4.60464631) + (1.22539002 + 0.38927567) * edad + cte\)
    2. \(E_m(peso) =-70.1692074 + 1.61466569 * edad + cte\)

Finalmente, graficamos ambas rectas definiendo la \(cte\) con un valor que de pesos positivos para tener una gráfica consistente:

cte = 100

train_set %>% 
  mutate(
    peso = ifelse(
      genero=='Femenino', 
      (-65.56456109 + 1.22539002 * edad) + cte,
      (-70.1692074 + 1.61466569 * edad) + cte
    ) 
  ) %>%
  ggplot(aes(x = edad, y = peso, colour=genero)) +
  geom_line() +
  ylab('Peso') +
  xlab('Edad')

Finalmente, se puede apreciar que las ordenadas de ambos géneros son distintas, donde el genero femenino inicia desde un peso menor al masculino. Luego si variamos únicamente la edad, se aprecia que el peso del genero masculino es mayor al femenino para la misma edad en todo los casos. Esto se debe a que la resta correspondiente al genero masculina esta por arriba de la resta correspondiente al genero femenino.

3.3. ¿Qué porcentaje de la variabilidad explica el modelo? En caso de detectar que existen categorías

no significativas de la variable consumo_semanal_snacks evaluar si la variable es significativa en su conjunto y, en caso afirmativo, proponer una redefinición de las mismas que permita obtener una mayor proporción de categorías significativas individualmente. Luego, analizar si existen cambios en la variabilidad explicada por el modelo.

Viendo el resultado de coefficients_summary se aprecia que las siguientes categorías de consumo_semanal_snacks no son significativas:

  • 2 veces al día (14 veces/semana)
  • 4 a 6 veces durante los últimos 7 (4 a 6 veces semana)
  • 3 veces al día (21 veces/semana)

Pero si son significativas los extremos:

  • De 1 a 3 veces/semana
  • De 28 o mas veces/semana

A continuación se realiza un \(F\) test para evaluar la significatividad conjunta de las categóricas de la variable consumo_semanal_snacks para explicar el peso.

El \(F\) test también llamando ANOVA (Análisis de la varianza) se realiza para probar la significatividad conjunta de todos los valores de una variable categórica.

Las hipótesis son las siguientes:

  • \(H_0: β_q = β_{q+1} = · · · = β_{p−1} = 0\)
  • \(H_1:\) por lo menos uno de los \(β_k\) (con \(k\) entre \(q\) y \(p−1\)) es tal que \(β_k \neq 0\).

Luego si todos los coeficientes asociados a los valores de variable categórica son cero, se rechaza la hipótesis nula y por lo tanto la variable no es significartiva para explicar el peso en nuestro caso.

A continuación veremos el p-valor resultado de aplicar \(F\) test para cada variable del modelo:

anova_summary(model_2)

Podemos apreciar que el \(p-value < 0.005\) para la variable consumo_semanal_snacks. Por lo tanto se rechaza la hipótesis nula y podemos decir en su conjunto resulta estadísticamente significativa para explicar el peso. Luego, como la variable consumo_semanal_snacks es significativa vale la pena re-definirla. Por otro lado, la combinación de variables genero-edad no es estadísticamente significativa para explicar el peso, pero si lo es el genero en forma separada. Finalmente, como ya vimos en pasos anteriores, edad y altura son significativas.

Modelo 2: Redefinición 1

Dado que no todas las categorías de la variable consumo_semanal_snacks sin significativas a continuación se propone una re-definición de sus categorías que hace que todas ellas sean significativas para el modelo 2.

train_set_snack_1 <- train_set %>% mutate(consumo_semanal_snacks = case_when(
  consumo_semanal_snacks %in% c('<=3', '4-6' , '7')  ~ '<=7',
  consumo_semanal_snacks %in% c('14', '21', '>=28')  ~ '>=14',
  TRUE ~ as.character(consumo_semanal_snacks)
))
train_set_snack_1$consumo_semanal_snacks <- factor(
  train_set_snack_1$consumo_semanal_snacks,
  levels=c('0', '<=7', '>=14'), 
  ordered=FALSE
)
table(train_set_snack_1$consumo_semanal_snacks) 

   0  <=7 >=14 
2162 4371  465 
model_2_redefinicion_1 <- lm(
  peso ~ altura + edad + genero + consumo_semanal_snacks +  genero * edad, 
  data = train_set_snack_1
)

coefficients_summary(model_2_redefinicion_1)
___________________________________________________________________________________________________ 
term                          estimate  std.error  statistic       p.value     conf.low   conf.high 
=================================================================================================== 
(Intercept)                -64.0389167 2.83445628 -22.593016 3.806812e-109 -69.59531089 -58.4825225 
altura                       0.6419791 0.01456558  44.075074  0.000000e+00   0.61342615   0.6705321 
edad                         1.2234673 0.12139076  10.078752  9.956603e-24   0.98550459   1.4614300 
generoMasculino             -4.6552035 2.68449385  -1.734108  8.294292e-02  -9.91762586   0.6072189 
consumo_semanal_snacks<=7   -1.3800004 0.26062486  -5.294968  1.226632e-07  -1.89090421  -0.8690966 
consumo_semanal_snacks>=14  -1.5602043 0.50675759  -3.078798  2.086432e-03  -2.55360293  -0.5668057 
edad:generoMasculino         0.3928142 0.17941300   2.189441  2.859775e-02   0.04111025   0.7445181 
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ 
_______________________________________________________________________ 
Termino                    Coeficiente Signiticativo IC incluye al cero 
======================================================================= 
(Intercept)                -64.0389167            Si                 No 
altura                       0.6419791            Si                 No 
edad                         1.2234673            Si                 No 
generoMasculino             -4.6552035            No                 Si 
consumo_semanal_snacks<=7   -1.3800004            Si                 No 
consumo_semanal_snacks>=14  -1.5602043            Si                 No 
edad:generoMasculino         0.3928142            Si                 No 
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ 

anova_summary(model_2_redefinicion_1)
glance(model_2_redefinicion_1)

Modelo 2: Redefinición 2

En este caso se propone calcular la media del ratio altura/edad para cada categoría de la variables consumo_semanal_snacks. Luego calculamos los cuantiles de esta nueva distribución y los utilizamos para crear una nueva categorización: los individuos que tenga un ratio menor al cuantil 2 tendran el valor Bajo y Alto en caso contrario. Se intento llevar a mas niveles pero el test \(T\) no daba significativo para todos los coeficientes del modelo 2.

  1. Definimos la districión altura/edad por categoria de consumo_semanal_snacks:
train_set_snack_2 <- train_set %>% 
  mutate(alt_edad_ratio = round(altura/edad, 0))

avg_train_set_snack_2 <- train_set_snack_2 %>% 
   group_by(consumo_semanal_snacks) %>% 
   summarise(avg_alt_edad_ratio = mean(alt_edad_ratio))

ggplot(data = avg_train_set_snack_2, aes(x = avg_alt_edad_ratio)) + 
  geom_boxplot(alpha = 0.75, fill="blue") +
  theme_bw()

Se los siguientes cuantiles utilizaremos el cuantil 2(50%):

quantiles_avg_alt_edad_ratio <- quantile(avg_train_set_snack_2$avg_alt_edad_ratio)
quantiles_avg_alt_edad_ratio
      0%      25%      50%      75%     100% 
10.73077 10.89892 10.97694 11.03058 11.09952 
  1. Creamos un dataset intermedio donde estan mapeadas las categorias originales vs las nuevas:
q2 <- quantiles_avg_alt_edad_ratio[3]

snack_level_mapping <- avg_train_set_snack_2 %>% 
  mutate(level = case_when(
    avg_alt_edad_ratio < q2  ~ 'Bajo',
    avg_alt_edad_ratio >=  q2 ~ 'Alto'
  )) %>% select(consumo_semanal_snacks, level)

snack_level_mapping %>%
  arrange(consumo_semanal_snacks)
  1. Creamos nuevos dataset de trasin/test con la nueva definición de la variable consumo_semanal_snacks:
train_set_snack_2 <- train_set %>%
  inner_join(snack_level_mapping, by = 'consumo_semanal_snacks') %>%
  mutate(consumo_semanal_snacks = level) %>% 
  select(-level)

test_set_snack_2 <- test_set %>%
  inner_join(snack_level_mapping, by = 'consumo_semanal_snacks') %>%
  mutate(consumo_semanal_snacks = level) %>% 
  select(-level)
train_set_snack_2 %>%  
  group_by(consumo_semanal_snacks) %>% 
  tally()

test_set_snack_2 %>%  
  group_by(consumo_semanal_snacks) %>% 
  tally()
  1. Evaluamos el nuevo train_set con el modelo 2:
model_2_redefinicion_2 <- lm(
  peso ~ altura + edad + genero + consumo_semanal_snacks +  genero * edad, 
  data = train_set_snack_2
)

coefficients_summary(model_2_redefinicion_2)
___________________________________________________________________________________________________ 
term                          estimate  std.error  statistic       p.value     conf.low   conf.high 
=================================================================================================== 
(Intercept)                -65.6507415 2.81532257 -23.319083 6.616674e-116 -71.16962416 -60.1318588 
altura                       0.6435785 0.01452076  44.321274  0.000000e+00   0.61511347   0.6720436 
edad                         1.2217889 0.12112912  10.086666  9.183448e-24   0.98433923   1.4592385 
generoMasculino             -4.6387828 2.67872503  -1.731713  8.336863e-02  -9.88989298   0.6123275 
consumo_semanal_snacksBajo   1.1231822 0.24872446   4.515769  6.411838e-06   0.63560713   1.6107573 
edad:generoMasculino         0.3929346 0.17903067   2.194789  2.821136e-02   0.04198041   0.7438888 
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ 
_______________________________________________________________________ 
Termino                    Coeficiente Signiticativo IC incluye al cero 
======================================================================= 
(Intercept)                -65.6507415            Si                 No 
altura                       0.6435785            Si                 No 
edad                         1.2217889            Si                 No 
generoMasculino             -4.6387828            No                 Si 
consumo_semanal_snacksBajo   1.1231822            Si                 No 
edad:generoMasculino         0.3929346            Si                 No 
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ 

anova_summary(model_2_redefinicion_2)
glance(model_2_redefinicion_2)
models <- list(
  'Modelo 1' = model_1, 
  'Modelo 2' = model_2, 
  'Modelo 2 - Re-definición 1' = model_2_redefinicion_1, 
  'Modelo 2 - Re-definición 2' = model_2_redefinicion_2
)

models %>% 
  map_df(glance, .id = "model") %>%
  arrange(desc(adj.r.squared))

Conclusión: Ambos modelos son significativos para explicar el peso. El modelo Modelo 2 - Re-definición 1 es mas explicativo, ya que \(R^2\) ajustado es mayor. Finamente, ambos modelos son meno explicativos que el modelo original(Modelo 2).

4. Modelos propios y evaluación

4.1. Realizar 2 modelos lineales múltiples adicionales y explicar breve-mente la lógica detrás de los mismos (se valorará la creación y/o inclusión de variables nuevas). Evaluar la performance del modelo inicial, el modelo categóricas con las categorías redefinidas de la variable consumo_semanal_snacks y los modelos desarrollados en este punto en el dataset de entrenamiento y evaluación (usar dataset “encuesta_salud_test.csv”). La evaluación de performance consiste en comparar en ambos sets la performance en términos del R cuadrado ajustado, RMSE y MAE.

Al continuación se define 2 modelos.

Modelo 4

\(E(peso) = \beta_0 + \beta_1 * altura + \beta_2 * edad + + \beta_3 * genero + \beta4 * consumoSemanalSnacks + \beta_5 * diasActividadFisicaSemanal + \beta_6 * altura * genero\)

Se utilizo la redefinición de la variable consumo_semanal_snacks como base. Ademase se agregar la variable dias_actividad_fisica_semanal entendiendo que tiene una influencia iportante en el peso y luego la asociacion altura * genero ya que en general mas mujeres tienen a ser mas bajar que los varones y vise versa.

model_4 <- lm(
  peso~ 
    altura + 
    edad + 
    genero + 
    consumo_semanal_snacks +
    dias_actividad_fisica_semanal +
    altura*genero,
  data = train_set_snack_1
)

coefficients_summary(model_4)
_______________________________________________________________________________________________________ 
term                              estimate  std.error  statistic       p.value    conf.low    conf.high 
======================================================================================================= 
(Intercept)                   -57.13540489 3.66317283 -15.597245  6.119582e-54 -64.3163351 -49.95447466 
altura                          0.58771694 0.02211420  26.576453 2.445371e-148   0.5443664   0.63106748 
edad                            1.36070412 0.09229317  14.743281  1.806969e-48   1.1797815   1.54162674 
generoMasculino               -15.91652545 4.63512139  -3.433896  5.984266e-04 -25.0027698  -6.83028113 
consumo_semanal_snacks<=7      -1.38916513 0.26043440  -5.334031  9.906841e-08  -1.8996956  -0.87863468 
consumo_semanal_snacks>=14     -1.51577303 0.50629126  -2.993876  2.764223e-03  -2.5082575  -0.52328854 
dias_actividad_fisica_semanal  -0.09518655 0.04988025  -1.908301  5.639321e-02  -0.1929670   0.00259388 
altura:generoMasculino          0.10477118 0.02825811   3.707649  2.108111e-04   0.0493767   0.16016566 
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ 
___________________________________________________________________________ 
Termino                        Coeficiente Signiticativo IC incluye al cero 
=========================================================================== 
(Intercept)                   -57.13540489            Si                 No 
altura                          0.58771694            Si                 No 
edad                            1.36070412            Si                 No 
generoMasculino               -15.91652545            Si                 No 
consumo_semanal_snacks<=7      -1.38916513            Si                 No 
consumo_semanal_snacks>=14     -1.51577303            Si                 No 
dias_actividad_fisica_semanal  -0.09518655            No                 Si 
altura:generoMasculino          0.10477118            Si                 No 
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ 

anova_summary(model_4)
glance(model_4)
train_set3 <- column_mean_quantile_binning(train_set_snack_1, 'dias_actividad_fisica_semanal')
test_set3  <- column_mean_quantile_binning(train_set_snack_1,  'dias_actividad_fisica_semanal')

train_set3 <- column_mean_quantile_binning(train_set3, 'consumo_semanal_frutas')
test_set3  <- column_mean_quantile_binning(test_set3,  'consumo_semanal_frutas')

train_set3 <- column_mean_quantile_binning(train_set3, 'consumo_semanal_verdura')
test_set3  <- column_mean_quantile_binning(test_set3,  'consumo_semanal_verdura')

train_set3 <- column_mean_quantile_binning(train_set3, 'consumo_semanal_comida_grasa')
test_set3  <- column_mean_quantile_binning(test_set3,  'consumo_semanal_comida_grasa')

train_set3 <- column_mean_quantile_binning(train_set3, 'consumo_semanal_gaseosas')
test_set3  <- column_mean_quantile_binning(test_set3,  'consumo_semanal_gaseosas')

segmented_box_plot(
  test_set3, 
  column        = 'peso', 
  segmented_by  = 'dias_actividad_fisica_semanal',
  title         = 'Niveles actividad fisica ordenados por la mediana del peso en Test',
  y_label       = 'Peso (Kg)',
  y_limits      = c(40, 100),
  x_label       = 'Niveles de actividad física (Dias)'
)

Modelo 5

\(E(peso) = \beta_0 + \beta_1 * altura + \beta_2 * edad + + \beta_3 * genero + \beta4 * consumoSemanalSnacks + \\ \beta_5 * diasActividadFisicaSemanal + \beta_6 * consumoSemanalFrutas + \beta_7 * consumoSemanalVerduras + \\* \beta_8 * consumoSemanalGrasas + \beta_9 * consumoSemanalGaseosas\)

Se utilizo la redefinición de la variable consumo_semanal_snacks como base. Ademase se agregar la variable consumo_semenal_frutras/verduras/grasas/gaseaosas entendiendo que también tiene una influencia importante en el peso.

model_5 <- lm(
  peso ~ 
  edad +
  genero +
  altura +
  consumo_semanal_snacks +
  consumo_semanal_frutas + 
  consumo_semanal_verdura,
  data = train_set3
)

coefficients_summary(model_5)
_____________________________________________________________________________________________________ 
term                           estimate  std.error   statistic       p.value    conf.low    conf.high 
===================================================================================================== 
(Intercept)                   9.8678389 1.98469390   4.9719702  6.785453e-07   5.9772367  13.75844105 
edad                          0.5848636 0.06830864   8.5620740  1.348627e-17   0.4509580   0.71876925 
generoMasculino               0.0245439 0.19988924   0.1227875  9.022789e-01  -0.3672997   0.41638746 
altura                        0.3042205 0.01142257  26.6332690 6.171857e-149   0.2818288   0.32661222 
consumo_semanal_snacks<=7    -0.8350398 0.19147128  -4.3611750  1.312220e-05  -1.2103815  -0.45969796 
consumo_semanal_snacks>=14   -0.7064861 0.37220275  -1.8981216  5.772121e-02  -1.4361164   0.02314422 
consumo_semanal_frutasBajo  -15.8033155 0.20429808 -77.3542056  0.000000e+00 -16.2038017 -15.40282928 
consumo_semanal_verduraBajo          NA         NA          NA            NA          NA           NA 
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ 
________________________________________________________________________ 
Termino                     Coeficiente Signiticativo IC incluye al cero 
======================================================================== 
(Intercept)                   9.8678389            Si                 No 
edad                          0.5848636            Si                 No 
generoMasculino               0.0245439            No                 Si 
altura                        0.3042205            Si                 No 
consumo_semanal_snacks<=7    -0.8350398            Si                 No 
consumo_semanal_snacks>=14   -0.7064861            No                 Si 
consumo_semanal_frutasBajo  -15.8033155            Si                 No 
consumo_semanal_verduraBajo          NA            NA                 NA 
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ 

anova_summary(model_5)
glance(model_5)
c(models, list('Modelo 4'=model_4, 'Modelo 5'=model_5)) %>% 
  map_df(glance, .id = "model") %>%
  arrange(desc(adj.r.squared))

Finalmente, si comparamos los modelos por \(R^2\) Ajustado, se puede apreciar que el modelo 5 (con todas las variables categóricas re-definidas) llega a captar la mayor varianza explicada sobre el dataset de entrenamiento. Por supuesto esto no dice nada acerca de la performance del modelo en test, pero si que tiene la mejor capacidad para extraer información de los dato de entrenamiento.

4.2. ¿Cuál es el mejor modelo para nuestro objetivo de predecir el peso? ¿Por qué?

Ahora comparamos la performance de todo los modelos al evaluar el error delos mismo al predecir el peso en el conjunto de train y test tanto para RMSE como MAE:

RMSE

custom_models_evaluation_summary(
  model_1, model_2, model_2_redefinicion_1, model_4, model_5,
  test_set, train_set_snack_1, test_set3,
  metric_fn = rmse
)

Si utilizamos la métrica RMSE podemos ver que el modelo 5 tiene el menor error en el conjunto de test. Por otro lados el que tiene la mayor diferencia de error entre test y entrenamiento. Esto nos dice que podría estar sobre-ajustandose al conjunto de entrenamiento. El modelo 3 tiene un error en test muy cercano y ademas tiene un diferencia entre test y train mucho menor. por esto ultimo parece ser el mejor modelo ya que tiene prácticamente el menor error posible y también el menor sobre-ajuste al conjunto de entrenamiento.

MAE

custom_models_evaluation_summary(
  model_1, model_2, model_2_redefinicion_1, model_4, model_5,
  test_set, train_set_snack_1, test_set3,
  metric_fn = mae
)

Si medimos a partir del MAE sucede algo muy similar, El modelo 3 es es que tiene menor error y ademas menos sobre-ajuste.

Finalmente, según ambas metricas el moejor modelo es el Modelo 3.

5. Diagnóstico del modelo

Analizar en profundidad el cumplimiento de los supuestos del modelo lineal para el modelo inicial.

plot(model_1)

Homocedasticidad

Al visualizar el primer gráfico (Residuos vs. Valores ajustados) se puede apreciar que hay presencia de homocedasticidad, ya que a medida que aumentan los valores predichos la variabilidad o amplitud de los residuos parece mantenerse en los mismo niveles. Dadas esta condiciones podemos decir que se cumple el supuesto de varianza constante.

Normalidad

Al visualizar el diagrama QQ-Plot podemos observas que en el extremo derecho, el modelo sobre-estima el peso del los individuos ya que hay una gran diferencia entre los valores predichos y los valores esperados teóricos. Lo contrario sucede a izquierda, donde el modelo subestima el valor de peso en comparación al valor esperado teórico, aunque los valores de los residuos son menores en este caso. Finalmente el QQ-Plot muestra un grado de alejamiento pronunciado de una distribución normal teórica y por lo tanto no se cumple el supuesto de normalidad del modelo.

Apalancamiento (Leverage)

Si observamos el gráfico de Residuos vs Apalacamiento vemos varias observaciones o individuos que se alejan a derecha del cumulo principal. Estos ejercen un alejamiento de las prediciones del modelo vs los valores reales a partir de un apalancamiento(leverage) 0.0025 y es mas pronunciado desde 0.0035. Finalmente, vemos un grado importante de desvió de las predicciones vs valores reales y porn ente un grado importante de apalancamiento(leverage).

A continuación se pueden ver lo individuos que producen mayor apalancamiento(leverage) y por ende sesgo en las predicciones del modelo:

augment(model_1) %>%
  filter(.hat>0.0025) %>%
  arrange(.hat)

6. Modelo Robusto

Leer el archivo “encuesta_salud_modelo6.csv”. Este último consiste en el dataset original de train con la incorporación de algunas observaciones adicionales que pueden incluir valores atípicos. En particular, observar la relación entre peso y altura ¿Qué ocurre con estos nuevos datos? Entrenar el modelo inicial con estos nuevos datos y comentar qué se observa en los coeficientes estimados y las métricas de evaluación (R cuadrado ajustado, RMSE y MAE) respecto al modelo entrenado con el set de entrenamiento original. Entrenar un modelo robusto con la misma especificación que el modelo inicial sobre los nuevos datos. Comparar los coeficientes y su performance (RMSE y MAE) respecto al modelo inicial no robusto entrenado en este punto. ¿Qué puede concluir al respecto?

Se carga el conjunto de entrenamiento en crudo,e s decir sin pre-procesamiento. Luego se resumen los valores de las variables categóricas y se eliminan missing values, ya que siguen siendo muy poco casos:

original_train_set <- shorten_values(preprocess(load_original_train_set()))
missings_summary(original_train_set)
new_train_set <- process_missings(original_train_set)
missings_summary(new_train_set)
nrow(original_train_set)
[1] 7060
nrow(new_train_set)
[1] 7060

Comparemos las distribuciones del peso vs altura en ambos conjunto de entrenamiento:

box_plots(
  train_set %>% select(peso, altura), 
  title = 'Comparativas de distribuciones del peso y la altura'
)


box_plots(
  new_train_set %>% select(peso, altura), 
  title = 'Comparativas de distribuciones del peso y la altura'
)

En el dataset de entrenamiento original la variable peso tiene prácticamente el doble de outliers que el dataset procesado.

Modelo 6

Definimos un modelo igual al modelo 1 pero entrenando en el dataset de entrenamiento original.

model_6 <- lm(
  peso ~ altura + edad + dias_actividad_fisica_semanal + consumo_diario_alcohol, 
  data = new_train_set
)

coefficients_summary(model_6)
__________________________________________________________________________________________________________ 
term                               estimate  std.error    statistic       p.value    conf.low    conf.high 
========================================================================================================== 
(Intercept)                   -73.226959301 2.64240181 -27.71227259 1.533910e-160 -78.4068604 -68.04705825 
altura                          0.687757405 0.01534488  44.81998610  0.000000e+00   0.6576768   0.71783798 
edad                            1.360298885 0.11300763  12.03723087  4.765114e-33   1.1387700   1.58182777 
dias_actividad_fisica_semanal  -0.095288639 0.06016347  -1.58382891  1.132775e-01  -0.2132271   0.02264982 
consumo_diario_alcohol          0.006469476 0.07426869   0.08710906  9.305873e-01  -0.1391195   0.15205841 
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ 
____________________________________________________________________________ 
Termino                         Coeficiente Signiticativo IC incluye al cero 
============================================================================ 
(Intercept)                   -73.226959301            Si                 No 
altura                          0.687757405            Si                 No 
edad                            1.360298885            Si                 No 
dias_actividad_fisica_semanal  -0.095288639            No                 Si 
consumo_diario_alcohol          0.006469476            No                 Si 
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ 

anova_summary(model_6)
glance(model_6)
print(paste('Disminicion de adj.r.squared:', abs(0.352113 - 0.2734821) * 100, '%'))
[1] "Disminicion de adj.r.squared: 7.86309 %"

Dada la presencia de outliers en la variable peso, el \(R^2\) Ajustado baja con respecto al modelo 1.

models <- list('Modelo 6'=model_6)

models_evaluation_summary(models, train_set, metric_fn = rmse)
models_evaluation_summary(models, train_set, metric_fn = mae)

Por otro lado, aumento el error de predicción tanto en train como en test. Finalmente, el modelo tiene un grado de overfitting mucho mayor que los modelos anteriores, ya que la métrica de evaluación en test y train tiene una diferencia muy pronunciada de 1.7 puntos.

Modelo 7

Definimos un modelo igual al modelo 1 entrenando en el dataset de entrenamiento original y usamos un modelo lineal robusto.

model_7 <- rlm(
  peso ~ altura + edad + dias_actividad_fisica_semanal + consumo_diario_alcohol, 
  data = new_train_set
)
coefficients_summary(model_7)
___________________________________________________________________________________________ 
term                              estimate  std.error   statistic     conf.low    conf.high 
=========================================================================================== 
(Intercept)                   -69.48061301 2.01139614 -34.5434753 -73.42287701 -65.53834901 
altura                          0.66495747 0.01168052  56.9287383   0.64206407   0.68785088 
edad                            1.26361070 0.08602140  14.6894924   1.09501186   1.43220955 
dias_actividad_fisica_semanal  -0.01588531 0.04579643  -0.3468679  -0.10564466   0.07387404 
consumo_diario_alcohol          0.03260215 0.05653332   0.5766891  -0.07820113   0.14340542 
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ 
[1] "WARN: p.value column is required to make model coefficients summary!\n"
[1] "WARN: p.value column is required to plot tidy coefficients!\n"
NULL
anova_summary(model_7)
models <- list('Modelo 6'=model_6, 'Modelo 7'=model_7)

models_evaluation_summary(models, test_set, metric_fn = rmse)
models_evaluation_summary(models, test_set, metric_fn = mae)

El modelo lineal robusto (Modelo 7) parece tener un menor error de entrenamiento muy cercano al modelo 6, pero tiene mayor sobre- ajuste que el modelo 6, aunque es una diferencia muy baja.

Dado esto, seria una buena selecciono elegir el modelo 7, ya que el sobre ajuste practicamente no cambia y obtenemos un error de predicción en test ligeramente menor.

LS0tCnRpdGxlOiB8CiAgICB8IE1hZXN0csOtYSBlbiBFeHBsb3RhY2nDs24gZGUgZGF0b3MgeSBEZXNjdWJyaW1pZW50byBkZSBjb25vY2ltaWVudG8KICAgIHwKICAgIHwKICAgIHwgTWF0ZXJpYTogRW5mb3F1ZSBFc3RhZGlzdGljbyBkZWwgQXByZW5kaXphamUKICAgIHwgVHJhYmFqbyBwcsOhY3RpY28gMTogUmVncmVzaW9uIExpbmVhbAphdXRob3I6ICJBZHJpYW4gTm9yYmVydG8gTWFyaW5vIgpkYXRlOiAiMjAyMS8wOS8xOSIKZmlnX3dpZHRoOiAzIApmaWdfaGVpZ2h0OiAzIApvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIGhpZ2hsaWdodDogcHlnbWVudHMKICAgIHRoZW1lOiBzYW5kc3RvbmUKICAgIHRvYzogeWVzCiAgICBkZl9wcmludDogcGFnZWQKICAgIGluY2x1ZGVzOgogICAgICBiZWZvcmVfYm9keTogLi9oZWFkZXIuaHRtbAogICAgICBhZnRlcl9ib2R5OiAuL2Zvb3Rlci5odG1sCiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwogICAgZGZfcHJpbnQ6IHBhZ2VkCmVkaXRvcl9vcHRpb25zOiAKICBtYXJrZG93bjogCiAgICB3cmFwOiAxMDAKLS0tCgojIyAyLiBNb2RlbG8gaW5pY2lhbAoKIyMjIDIuMS4gU2UgcGxhbnRlYSBsYSBzaWd1aWVudGUgcHJpbWVyYSBhbHRlcm5hdGl2YSBwYXJhIG1vZGVsYXIgZWwgcGVzbzoKCiRFKHBlc28pID0gXGJldGFfMCArIFxiZXRhXzEgKiBhbHR1cmEgKyBcYmV0YV8yICogZWRhZCArIFxiZXRhXzMgKiBnZW5lcm8gKyBcYmV0YTQgKiBkaWFzQWN0aXZpZGFkRmlzaWNhU2VtYW5hbCArIFxiZXRhNSAqIGNvbnN1bW9EaWFyaW9BbGNvaG9sJAoKUHJpbWVybyBzZSBjYXJnYW4gbGFzIGxpYnJlcsOtYXMgbmVjZXNhcmlhczoKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9Cm9wdGlvbnMod2Fybj0tMSkKcm0obGlzdD1scygpKQpnYygpCm9wdGlvbnMod2Fybj0tMikKYGBgCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIGluc3RhbGwucGFja2FnZXMoInBhY21hbiIpIC0tIERlc2NvbWVudGFyIHBhciBpbnN0YWxhciBwYWNtYW4KbGlicmFyeShwYWNtYW4pCnBfbG9hZF9naCgnYWRyaWFubWFyaW5vL2NvbW1vbnMnKQppbXBvcnQoJy4uL3NyYy9kYXRhc2V0LlInKQppbXBvcnQoJy4uL3NyYy9wcmVwcm9jZXNzaW5nLlInKQppbXBvcnQoJy4uL3NyYy9tb2RlbC5SJykKaW1wb3J0KCcuLi9zcmMvcGxvdC5SJykKYGBgCgpBIGNvbnRpbnVhY2nDs24gc2UgY2FyZ2EgbG9zIGNvbmp1bnRvcyBkZSBlbnRyZW5hbWllbnRvIHkgdGVzdC4gdGFtYmnDqW4gc2UgcmVzdW1lbiBsb3MgdmFsb3JlcyBkZSBsYXMKdmFyaWFibGVzIGNhdGVnw7NyaWNhcyB5IHNlIGV4Y2x1eWVuIGxhcyBvYnNlcnZhY2lvbmVzIGNvbiB2YWxvcmVzIGZhbHRhbnRlcywgeWEgcXVlIHNvbiBtdXkgcG9jYXMKY29uIHJlc3BlY3RvIGFsIHRvdGFsLgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KdHJhaW5fc2V0IDwtIGxvYWRfdHJhaW5fc2V0KCkgJT4lIAogIHByZXByb2Nlc3MoKSAlPiUgCiAgc2hvcnRlbl92YWx1ZXMoKSAlPiUKICBwcm9jZXNzX21pc3NpbmdzKCkKCnRlc3Rfc2V0IDwtIGxvYWRfdGVzdF9zZXQoKSAlPiUgCiAgcHJlcHJvY2VzcygpICU+JSAKICBzaG9ydGVuX3ZhbHVlcygpICU+JSAKICBwcm9jZXNzX21pc3NpbmdzKCkKYGBgCgpgYGB7cn0KZ2xpbXBzZSh0cmFpbl9zZXQpCmBgYAoKU2UgZmlqYSBsYSBzZW1pbGxhIHkgc2UgdmFsaWRhbiBsYXMgcHJvcG9yY2lvbmVzIGRlIGxvcyBjb25qdW50b3MgZGUgZW50cmVuYW1pZW50byB5IHRlc3Q6CgpgYGB7cn0Kc2V0LnNlZWQoMjUpCnNob3dfdHJhaW5fdGVzdF9wcm9wcyh0cmFpbl9zZXQsIHRlc3Rfc2V0KQpgYGAKCioqTW9kZWxvIDEqKgoKU2UgcGxhbnRlYSBlbCBwcmltZXIgbW9kZWxvIGxpbmVhbDoKCmBgYHtyfQptb2RlbF8xIDwtIGxtKAogIHBlc28gfiBhbHR1cmEgKyBlZGFkICsgZ2VuZXJvICsgZGlhc19hY3RpdmlkYWRfZmlzaWNhX3NlbWFuYWwgKyBjb25zdW1vX2RpYXJpb19hbGNvaG9sLCAKICBkYXRhID0gdHJhaW5fc2V0CikKYGBgCgojIyMgMi4yLiDCv0N1w6FsIGVzIGxhIGludGVycHJldGFjacOzbiBkZSBjYWRhIHVubyBkZSBsb3MgY29lZmljaWVudGVzIGVzdGltYWRvcz8KClZlYW1vcyBhIGNvbnRpbnVhY2nDs24gdW4gcmVzdW1lbiBkZSBsb3MgY29lZmljaWVudGUgZGVsICoqbW9kZWxvIDEqKjoKCmBgYHtyIGZpZy5oZWlnaHQ9MywgZmlnLndpZHRoPTYsIHdhcm5pbmc9RkFMU0UsIGZpZy5hbGlnbj0nY2VudGVyJ30KY29lZmZpY2llbnRzX3N1bW1hcnkobW9kZWxfMSkKYGBgCgpBbCBhbmFsaXphciBjYWRhIGNvZWZpY2llbnRlIHNlIGVuY3VlbnRyYSBxdWU6CgotICAgJFxoYXR7XGJldGFfMH0kIChPcmRlbmFkYSBhbCBvcmlnZW4pIGRlIHZhbG9yIC02OC45MiBLZywgZXMgZWwgcGVzbyBlc3BlcmFkbyBvIHByb21lZGlvIGRlIHVuCiAgICBpbmRpdmlkdW8gZGUgZ2VuZXJvIGZlbWVuaW5vIHF1ZSB0aWVuZSBjZXJvIGFsdHVyYSwgZWRhZCwgYWN0aXZpZGFkIGbDrXNpY2EgeSBjb25zdW1vIGRpYXJpbyBkZQogICAgYWxjb2hvbC4gRXN0byBubyBlcyBpbnRlcnByZXRhYmxlLCB5YSBxdWUgdW5hIHBlcnNvbmEgdGllbmUgcXVlIHRlbmVyIHVuYSBhbHR1cmEgc3VwZXJpb3IgYSBjZXJvCiAgICB5IG5vIHB1ZWRlIHRlbmVyIHVuIHBlc28gbmVnYXRpdm8sIHBlcm8gc2kgcG9kcsOtYSBubyByZWFsaXphciBhY3RpdmlkYWQgZsOtc2ljYSBuaSBjb25zdW1pcgogICAgYWxjb2hvbC4KCi0gICBFbCBjb2VmaWNpZW50ZSAkXGhhdHtcYmV0YV8xfSQgZGUgdmFsb3IgNjUzIGdyYW1vcywgY29ycmVzcG9uZGUgYSBsYSBhbHR1cmEgZGVsIGluZGl2aWR1by4gRXN0ZQogICAgY29lZmljaWVudGUgaW5kaWNhIHF1ZSBkYWRhIHVuYSBlZGFkLCBnZW5lcm8sIGNvbnN1bW8gZGUgYWxjb2hvbCBkaWFyaW8geSBkw61hcyBkZSBhY3RpdmlkYWQKICAgIGbDrXNpY2Egc2VtYW5hbCBmaWpvcywgY2FkYSBpbmNyZW1lbnRvIGVuIDEgY20gYWRpY2lvbmFsIGVuIGxhIGFsdHVyYSBkZWwgaW5kaXZpZHVvIGltcGxpY2EgdW4KICAgIGF1bWVudG8gZGUgc3UgcGVzbyBlc3BlcmFkbyBvIHByb21lZGlvIGRlIDY1MyBncmFtb3MuCgotICAgRWwgY29lZmljaWVudGUgJFxoYXR7XGJldGFfMn0kIGRlIHZhbG9yIDEuMzc4IGtnLCBjb3JyZXNwb25kZSBhIGxhIGVkYWQgZGVsIGluZGl2aWR1by4gRXN0ZQogICAgY29lZmljaWVudGUgaW5kaWNhIHF1ZSBkYWRhIHVuYSBhbHR1cmEsIGdlbmVybywgZMOtYXMgZGUgYWN0aXZpZGFkIGbDrXNpY2EgeSBjb25zdW1vIGRlIGFsY29ob2wKICAgIGRpYXJpbyBmaWpvcywgY2FkYSB2ZXogcXVlIGVsIGluZGl2aWR1byBjdW1wbGUgdW4gYcOxbyBzdSBwZXNvIGVzcGVyYWRvIG8gcHJvbWVkaW8gYXVtZW50YSBlbgogICAgMS4zNzgga2cuCgotICAgRWwgY29lZmljaWVudGUgJFxoYXR7XGJldGFfM30kIGRlIHZhbG9yIDEuMjI0IGtnLCBjb3JyZXNwb25kZSBhIGxvcyBpbmRpdmlkdW9zIGRlIGdlbmVybwogICAgbWFzY3VsaW5vcy4gRXN0ZSBjb2VmaWNpZW50ZSBpbmRpY2EgcXVlIGRhZGEgdW5hIGFsdHVyYSwgZWRhZCwgY29uc3VtbyBkZSBhbGNvaG9sIGRpYXJpbyB5IGTDrWFzCiAgICBkZSBhY3RpdmlkYWQgZsOtc2ljYSBzZW1hbmFsIGZpam9zLCBlbCBwZXNvIHByb21lZGlvIG8gZXNwZXJhZG8gcGFyYSBlbCBnZW5lcm8gbWFzY3VsaW5vIGVzIDEuMjI0CiAgICBrZyBtYXlvciBhbCBwZXNvIGZlbWVuaW5vIChjYXRlZ29yw61hIGJhc2FsKS4gUG9yIG90cm8gbGFkbywgZWwgY29lZmljaWVudGVzIG5vIGluZGljYSBjdW5hZG8gbWFzCiAgICBhbHRvIGVzIGVsIHBlc28gZGVsIGdlbmVybyBtYXNjdWxpbm8gcmVzcGVjdG8gZGVsIGZlbWVuaW5vIGFsIGZpamFyIGxvcyBkZW3DoXMgY29lZmljaWVudGVzLgoKLSAgIEVsIGNvZWZpY2llbnRlICRcaGF0e1xiZXRhXzR9JCBkZSB2YWxvciA5OS4xIGdyYW1vcywgY29ycmVzcG9uZGUgYSBsb3MgZMOtYXMgZGUgYWN0aXZpZGFkIGbDrXNpY2EKICAgIHNlbWFuYWwgcXVlIHJlYWxpemEgZWwgaW5kaXZpZHVvLiBFc3RlIGNvZWZpY2llbnRlIGluZGljYSBxdWUgZGFkYSB1bmEgYWx0dXJhLCBlZGFkLCBnZW5lcm8geQogICAgY29uc3VtbyBkZSBhbGNvaG9sIGRpYXJpbywgY2FkYSB2ZXogcXVlIHVuIGluZGl2aWR1byByZWFsaXphIHVuIGTDrWEgbWFzIGRlIGFjdGl2aWRhZCBmw61zaWNhCiAgICBzZW1hbmFsIHN1IHBlc28gZXNwZXJhZG8gbyBwcm9tZWRpbyBkaXNtaW51eWUgZW4gOTkuMSBncmFtb3MuCgotICAgRWwgY29lZmljaWVudGUgJFxoYXR7XGJldGFfNX0kIGRlIHZhbG9yIC04IGdyYW1vcywgY29ycmVzcG9uZGUgYWwgbml2ZWwgZGUgY29uc3VtbyBkaWFyaW8gZGUKICAgIGFsY29ob2wgZGVsIGluZGl2aWR1by4gRXN0ZSBjb2VmaWNpZW50ZSBpbmRpY2EgcXVlIGRhZGEgdW5hIGFsdHVyYSwgZWRhZCwgZ2VuZXJvIHkgZMOtYXMgZGUKICAgIGFjdGl2aWRhZCBmw61zaWNhIHNlbWFuYWwgZmlqb3MsIGNhZGEgdmV6IHF1ZSBlbCBpbmRpdmlkdW8gY29uc3VtZSB1biB0cmFnbyBkZSBhbGNvaG9sIHN1IHBlc28KICAgIGVzcGVyYWRvIG8gcHJvbWVkaW8gZGlzbWludXllIGVuIDggZ3JhbW9zLiBBIHNpbXBsZSB2aXN0YSBwb2Ryw6Egbm8gbGxlZ2FyIGEgdGVuZXIgc2VudGlkbywgeWEKICAgIHF1ZSBhIG1heW9yIGNvbnN1bW8gZGUgYWxjb2hvbCBlbCBwZXNvIGRlYmVyw61hIGF1bWVudGFyLCB5YSBzZWEgcG9yIGVsIHBlc28gZGVsIHByb3BpbyBsaXF1aWRvCiAgICBjb21vIGVsIHBlc28gZXF1aXZhbGVudGUgZW4gZ3Jhc2FzLiBFbnRpZW5kbyBxdWUgcHVlZGUgdGVuZXIgdW4gcmVsYWNpw7NuIGNvbiBsb3MgcmFuZ29zIGRlCiAgICBlZGFkZXMgZGUgbG9zIGluZGl2aWR1b3MgcXVlIG1hcyBjb25zdW1lbiBhbGNvaG9sICgxMiBxIDE3IGHDsW9zKSwgeWEgcXVlIGVzdG9zIHNlIGVuY3VlbnRyYW4gZW4KICAgIHBsZW5vIGNyZWNpbWllbnRvLgoKIyMjIDIuMy4gwr9Tb24gc2lnbmlmaWNhdGl2b3MgbG9zIGNvZWZpY2llbnRlcz8KClBhcmEgZGV0ZXJtaW5hIHNpIGxvcyBjb2VmaWNpZW50ZXMgc29uIGFwdG9zIHBhcmEgZXhwbGljYXIgZWwgcGVzbyBkZSB1biBpbmRpdmlkdW8gc2UgcmVhbGl6YSB1bgoke1R9JCB0ZXN0IHBhcmEgY2FkYSBjb2VmaWNpZW50ZSBlbiBlbCBjdWFsIHNlIGV2YWzDumFuIGxhcyBzaWd1aWVudGVzIGhpcMOzdGVzaXM6CgotICAgJHtIXzA6IFxiZXRhX2kgPSAwfSQKLSAgICR7SF8xOiBcYmV0YV9pIFxuZXEgMH0kCgpTaSAke1xiZXRhX2kgXG5lcSAwfSQgcG9kZW1vcyBkZWNpciBxdWUgZXhpc3RlIHVuYSBkaWZlcmVuY2lhIGVzdGFkaXN0aWNhbWVudGUgc2lnbmlmaWNhdGl2YXMgZGVsCmNlcm8gcGFyYSBjb2VmaWNpZW50ZSAke1xiZXRhX2l9JCwgeSBwb3IgbG8gdGFudG8gZWwgY29lZmljaWVudGUgJHtcYmV0YV9pfSQgZXhwbGljYXIgbGEgdmFyaWFibGUKJHt5fSQgKFBlc28gZW4gbnVlc3RybyBjYXNvKS4KCkx1ZWdvIGFuYWxpemFuZG8gbGEgc2FsaWRhIGRlICoqY29lZmZpY2llbnRzX3N1bW1hcnkqKiBjb25jbHVpbW9zIHF1ZToKCi0gICBMb3MgY29lZmljaWVudGVzIGNvcnJlc3BvbmRpZW50ZXMgYWwgYWNhdGVnb3JpYSBiYXNhbCAke1xiZXRhXzF9JChHZW5lcm8gZmVtZW5pbm8pLAogICAgYWx0dXJhKCR7XGJldGFfMX0kKSwgZWRhZCgke1xiZXRhXzJ9JCkgeSBnZW5lcm8gbWFzY3VsaW5vICgke1xiZXRhXzN9JCkgdGllbmVuICRwLXZhbG9yIDwgMC4wNSQuCiAgICBQb3IgbG8gdGFudG8sIHNlIHJlY2hhemEgbGEgaGlww7N0ZXNpcyBudWxhIHkgcmVzdWx0YW4gZXN0YWRpc3RpdGFtZW50ZSBzaWduaWZpY2F0aXZvcyBwYXJhCiAgICBleHBsaWNhciBlbCBwZXNvLiBQb3Igb3RybyBsYWRvLCBzZSBwdWVkZSBhcHJlY2lhciBxdWUgbG9zIGludGVydmFsb3MgZGUgY29uZmlhbnphIGRlbCA5NSUgbm8KICAgIGluY2x1eWVuIGFsIGNlcm8uCi0gICBMbyBjb250cmFyaW8gc3VjZWRlIGNvbiBkw61hcyBkZSBhY3RpdmlkYWQgZsOtc2ljYSBzZW1hbmFsKCR7XGJldGFfNH0kKSB5IGNvbnN1bW8gZGUgYWxjb2hvbAogICAgZGlhcmlvICgke1xiZXRhXzV9JCksIGRhZG8gcXVlIGFtYm9zIG5vIHJlY2hhemFyIGxhIGhpcMOzdGVzaXMgbnVsYSAoJHAtdmFsb3IgPiAwLjA1JCkgeSBwb3IgbG8KICAgIHRhbnRvIG5vIGV4aXN0ZSB1bmEgZGlmZXJlbmNpYSBzaWduaWZpY2F0aXZhIGRlbCBjZXJvLiBGaW5hbG1lbnRlLCBubyBoYXkgZXZpZGVuY2lhCiAgICBlc3RhZGlzdGl0YW1lbnRlIHNpZ25pZmljYXRpdmFzIGRlIHF1ZSBlc3RvcyBjb2VmaWNpZW50ZXMgZXhwbGlxdWVuIGFsIHBlc28uCgojIyMgMi40LiDCv0VsIG1vZGVsbyByZXN1bHRhIHNpZ25pZmljYXRpdm8gcGFyYSBleHBsaWNhciBlbCBwZXNvPwoKUGFyYSBkZXRlcm1pbmFyIHNpIGVzIG1vZGVsbyBlcyBzaWduaWZpY2F0aXZvIHBhcmEgZXhwbGljYXIgZWwgcGVzbyBkZSB1biBpbmRpdmlkdW8gc2UgcmVhbGl6YSB1bgokRiQgdGVzdCBjb24gbGFzIHNpZ3VpZW50ZXMgaGlww7N0ZXNpczoKCi0gICAkSF8wOiDOsl8xID0gzrJfMiA9IMK3IMK3IMK3ID0gzrJfe3DiiJIxfSA9IDAkCi0gICAkSF8xOiQgUG9yIGxvIG1lbm9zIHVuICTOsl9rJCAoJGsgPSAxLCAyLC4uLiwgcOKIkjEkKSBlcyBkaXN0aW50byBkZSAwLgoKRG9uZGU6IFwqICRIXzAkIGFmaXJtYSBxdWUgbm8gaGF5IHZpbmN1bG8gZW50cmUgbGEgdmFyaWFibGUgJHt5fSQoUGVzbykgeSBsYXMgdmFyaWFibGVzIHJlZ3Jlc29yYXMuClwqICRIXzEkIGFmaXJtYSBxdWUgYWwgbWVub3MgdW5hIGRlIGxhcyB2YXJpYWJsZXMgcmVncmVzb3JhcyBzaXJ2ZSBwYXJhIHByZWRlY2lyIGxhIHZhcmlhYmxlICR7eX0kCihQZXNvKS4KClZlYW1vcyBsb3MgcmVzdWx0YWRvcyBlbCAkRiQgdGVzdDoKCmBgYHtyfQpnbGFuY2UobW9kZWxfMSkKYGBgCgpQb2RlbW9zIGFwcmVjaWFyIHF1ZSBlbCAkcC12YWxvciA8IDAuMDUkIGUgaWd1YWwgYSAwLiBDb24gbXVjaGEgY2VydGV6YSBwb2RlbW9zIGRlY2lyIHF1ZSBhbCBtZW5vcwp1bmEgZGUgbGFzIHZhcmlhYmxlcyByZWdyZXNvcmFzIHBlcm1pdGUgZXhwbGljYXIgZWwgcGVzby4gRXN0byBjb25jdWVyZGEgY29uIGxvcyByZXN1bHRhZG9zIGRlIGxvcwokVCQgdGVzdCBwYXJhIGxhcyBsb3MgY29lZmljaWVudGVzIGNvcnJlc3BvbmRpZW50ZXMgYSBhbHR1cmEsIGVkYWQgeSBnZW5lcm8gZmVtZW5pbm8oYmFzYSkgeQptYXNjdWxpbm8pLgoKIyMjIDIuNS4gwr9RdcOpIHBvcmNlbnRhamUgZGUgbGEgdmFyaWFiaWxpZGFkIGV4cGxpY2EgZWwgbW9kZWxvPwoKU2Vnw7puIGVsIHZhbG9yIGRlICRSXjIkIGFqdXN0YWRvICgqKmFkai5yLnNxdWFyZWQqKiksIGVzdGUgbW9kZWxvIGxsZWdhIGEgZXhwbGljYSBlbCAzNSUgZGUgbGEKdmFyaWFiaWxpZGFkIGRlbCBkYXRhc2V0IGRlIGVudHJlbmFtaWVudG8sIGxvIGN1YWwgbm8gZXMgdW4gdmFsb3IgYmFqbyBwZXJvIHRhbXBvY28gZXMgZGVzcHJlY2lhYmxlLgoKIyMjIDIuNi4gwr9RdWUgc3VjZWRlIHNpIHBvbmVyIGFsIGdlbmVybyBtYXNjdWxpbm8gY29tbyB2YXJpYWJsZSBiYXNhbD8KCmBgYHtyfQp0cmFpbl9zZXRfZ2VuZXJvIDwtIGRhdGEuZnJhbWUodHJhaW5fc2V0KSAKdHJhaW5fc2V0X2dlbmVybyRnZW5lcm8gPC0gZmFjdG9yKAogIHRyYWluX3NldF9nZW5lcm8kZ2VuZXJvLAogIGxldmVscz1jKCdNYXNjdWxpbm8nLCAnRmVtZW5pbm8nKSwgCiAgb3JkZXJlZD1GQUxTRQopCnRhYmxlKHRyYWluX3NldF9nZW5lcm8kZ2VuZXJvKQpgYGAKCmBgYHtyfQptb2RlbF9nZW5lcm8gPC0gbG0oCiAgcGVzbyB+IGFsdHVyYSArIGVkYWQgKyBnZW5lcm8gKyBkaWFzX2FjdGl2aWRhZF9maXNpY2Ffc2VtYW5hbCArIGNvbnN1bW9fZGlhcmlvX2FsY29ob2wsIAogIGRhdGEgPSB0cmFpbl9zZXRfZ2VuZXJvCikKYGBgCgpgYGB7ciBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD02LCB3YXJuaW5nPUZBTFNFLCBmaWcuYWxpZ249J2NlbnRlcid9CmNvZWZmaWNpZW50c19zdW1tYXJ5KG1vZGVsX2dlbmVybykKYGBgCgpgYGB7cn0KZ2xhbmNlKG1vZGVsX2dlbmVybykKYGBgCgoqKk9ic2VydmFjaW9uZXMqKgoKLSAgIEVuIGVzdGUgY2FzbywgY2FtYmlhIGVsIGNvZWZpY2llbnRlICRcYmV0YV8wJCBwYXJhIGxhIGNhdGVnb3LDrWEgYmFzYWwsIG1hbnRlbmnDqW5kb3NlIGxvcyBkZW3DoXMKICAgIGNvZWZpY2llbnRlcy4KLSAgIFNpZ3VlbiBzaWVuZG8gc2lnbmlmaWNhdGl2b3MgbG9zIG1pc21vIGNvZWZpY2llbnRlcy4gTGEgdW5pY2EgZGlmZXJlbmNpYSBlcyBxdWUgJFxiZXRhXzAkKGJhc2FsKQogICAgcmVwcmVzZW50YSBhbCBnZW5lcm8gbWFzY3VsaW5vIHkgJFxiZXRhXzMkIGFsIGZlbWVuaW5vLgotICAgU2UgbWFudGllbmUgbGEgc2lnbmlmaWNhdGl2aWRhZCBnbG9iYWwgZGVsIG1vZGVsbyB5IGVsIG1pc21vICRSXjIkLgotICAgQ29tbyB1bHRpbW8sIGNhYmUgYWNsYXJhciBxdWUgYWwgY2FtYmlhciBsYSBjYXRlZ29yw61hIGJhc2FyIGNhbWJpYW4gbGFzIGludGVycHJldGFjaW9uZXMgZGVsCiAgICBtb2RlbG8uCgojIyAzLiBNb2RlbG8gY2F0ZWfDs3JpY2FzCgojIyMgMy4xLiBTZSBzdWdpZXJlIHByb2JhciB1biBtb2RlbG8gcXVlIGluY29ycG9yZSBlbCBjb25zdW1vIHNlbWFuYWwgZGUgc25hY2tzIHkgdW5hIGludGVyYWNjacOzbiBlbnRyZSBlbAoKZ8OpbmVybyB5IGxhIGVkYWQsIGVuIGx1Z2FyIGRlIGFjdGl2aWRhZCBmw61zaWNhIHkgY29uc3VtbyBkZSBhbGNvaG9sLiBBZGVtw6FzIHNlIHBpZGUgZXhwbGljaXRhbWVudGUKcXVlIGxhIGNhdGVnb3LDrWEgIk5vIGNvbcOtIGNvbWlkYSBzYWxhZGEgbyBzbmFja3MgZW4gbG9zIMO6bHRpbW9zIDcgZMOtYXMiIGRlIGxhIHZhcmlhYmxlCioqY29uc3Vtb19zZW1hbmFsX3NuYWNrcyoqIHNlIGVuY3VlbnRyZSBjb21vIG5pdmVsL2NhdGVnb3LDrWEgYmFzYWwuCgokRShwZXNvKSA9IFxiZXRhXzAgKyBcYmV0YV8xICogYWx0dXJhICsgXGJldGFfMiAqIGVkYWQgKyBcYmV0YV8zICogZ2VuZXJvICsgXGJldGE0ICogY29uc3Vtb1NlbWFuYWxTbmFja3MgKyBcYmV0YV81ICogZ2VuZXJvICogZWRhZCQKClByaW1lcm8gdmFsaWRhbW9zIHF1ZSBsYXMgcHJpbWVyYXMgY2F0ZWdvcsOtYXMgZW4gY2FkYSB2YXJpYWJsZSBkZSB0aXBvIGZhY3RvciBzZWFuIGxhcyBjb3JyZWN0YXMsIHlhCnF1ZSBlc3RhIHNlcmEgbGEgcXVlIGVsIG1vZGVsbyBkZWZpbmEgY29tbyBjYXRlZ29yw61hIGJhc2FsOgoKYGBge3J9CnRhYmxlKHRyYWluX3NldCRjb25zdW1vX3NlbWFuYWxfc25hY2tzKQp0YWJsZSh0cmFpbl9zZXQkZ2VuZXJvKQpgYGAKClNlIHB1ZWRlIGFwcmVjaWFyIHF1ZSBsYSBwcmltZXJhcyBjYXRlZ29yw61hcyBjb3JyZXNwb25kZW4gYSAwIGNvbnN1bW8gZGUgc25hY2tzIHNlbWFuYWwgeSBnZW5lcm8KZmVtZW5pbm8uIFBvciBvdHJvIGxhZG8gbGEgY2F0ZWdvcsOtYSBnZW5lcm8gc2UgZW5jdWVudHJhIGJhbGFuY2VhZGEuCgoqKk1vZGVsbyAyKioKCkRlZmluaW1vcyBlbCBudWV2byBtb2RlbG86CgpgYGB7cn0KbW9kZWxfMiA8LSBsbSgKICBwZXNvIH4gYWx0dXJhICsgZWRhZCArIGdlbmVybyArIGNvbnN1bW9fc2VtYW5hbF9zbmFja3MgKyAgZ2VuZXJvICogZWRhZCwgCiAgZGF0YSA9IHRyYWluX3NldAopCmBgYAoKIyMjIDMuMi4gwr9DdcOhbCBlcyBsYSBpbnRlcnByZXRhY2nDs24gZGUgbG9zIGNvZWZpY2llbnRlcyBlc3RpbWFkb3MgcGFyYSBsYXMgY2F0ZWdvcsOtYXMgZGUKCmNvbnN1bW9fc2VtYW5hbF9zbmFja3MgeSBnZW5lcm8gXCogZWRhZD8gwr9Tb24gc2lnbmlmaWNhdGl2YXM/CgpgYGB7ciBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD02LCB3YXJuaW5nPUZBTFNFLCBmaWcuYWxpZ249J2NlbnRlcid9CmNvZWZmaWNpZW50c19zdW1tYXJ5KG1vZGVsXzIpCmBgYAoKU2kgaW50ZXJwcmV0YW1vcyBsb3MgY29lZmljaWVudGVzIHF1ZSBzb24gc2lnbmlmaWNhdGl2b3MgcGFyYSBlbCAkVCQgdGVzdDoKCjEuICBDb2VmaWNpZW50ZSBjb3JyZXNwb25kaWVudGUgYWwgKipjb25zdW1vX3NlbWFuYWxfc25hY2tzXDw9MyoqOgoKU2kgZmlqYW1vcyBsb3MgY29lZmljaWVudGVzIGNvcnJlc3BvbmRpZW50ZXMgYSBsYSBhbHR1cmEsIGVkYWQsIGdlbmVyb01hc2N1bGlubyB5CmdlbmVyb01hc2N1bGlub1wqZWRhZDsgZWwgcGVzbyBwcm9tZWRpbyBvIGVzcGVyYWRvIGRlIHVuIGluZGl2aWR1byBkZSBjb25zdW1lIHNuYWNrcyBoYXN0YSAzIHZlY2VzCnBvciBzZW1hbmEgZXMgMS40MyBrZyBtZW5vciBxdWUgYXF1ZWxsb3MgcXVlIG5vIGNvbnN1bWVuIHNuYWNrcy4KCjIuICBTdWNlZGUgYWxnbyBzaW1pbGFyIGNvbiBsYXMgY2F0ZWdvcmlhcyAqKmNvbnN1bW9fc2VtYW5hbF9zbmFja3M0LTYqKiB5CiAgICAqKmNvbnN1bW9fc2VtYW5hbF9zbmFja3M+PTI4KiogZG9uZGU6CgotICAgRWwgcGVzbyBwYXJhIHVuIGluZGl2aWRpbyBxdWUgY29uc3VtZSBkZSA0IGEgNiB2ZWNlcyBwb3Igc2VtYW5hIGVzIDIuMjUga2cgbWVub3IgcXVlIGFxdWVsbG9zCiAgICBjb24gbm8gY29uc3VtZW4gc25hY2tzLgotICAgRWwgcGVzbyBwYXJhIHVuIGluZGl2aWRpbyBxdWUgY29uc3VtZSAyOCB2ZWNlcyBhIG1hcyBwb3Igc2VtYW5hIGVzIDIuNiBrZyBtZW5vciBxdWUgYXF1ZWxsb3MgY29uCiAgICBubyBjb25zdW1lbiBzbmFja3MuCgozLiAgQ29lZmljaWVudGUgY29ycmVzcG9uZGllbnRlIGFsICoqZWRhZCBcKiBnZW5lcm8qKjoKCkRhZG8gZWwgbW9kZWxvIG9yaWdpbmFsOgoKJEUocGVzbykgPSBcYmV0YV8wICsgXGJldGFfMSAqIGFsdHVyYSArIFxiZXRhXzIgKiBlZGFkICsgXGJldGFfMyAqIGdlbmVybyArIFwgXGJldGE0ICogY29uc3Vtb1NlbWFuYWxTbmFja3MgKyBcYmV0YV81ICogZ2VuZXJvICogZWRhZCQKCnkgc2FiaWVuZG8gcXVlIGVsIGdlbmVybyBmZW1lbmlubyB0b21hIGVsIHZhbG9yIDAgeSBtYXNjdWxpbm8gMS4gU2kgcmVlbXBsYXphbW9zIGVzdG9zIHZhbG9yZXMgZW4gZWwKbW9kZWxvIG9yaWdpbmFsIGVuY29udHJhbW9zIHF1ZToKCiRFX2YocGVzbykgPSBcYmV0YV8wICsgXGJldGFfMSAqIGFsdHVyYSArIFxiZXRhXzIgKiBlZGFkICsgXGJldGFfNCAqIGNvbnN1bW9TZW1hbmFsU25hY2tzJAoKRWwgZ2VuZXJvIGZlbWVuaW5vIHRpZW5lIGxhIG9yZGVuYWRhICRcYmV0YTAkIHkgbGFzIHBlbmRpZW50ZXMgZGV0ZXJtaW5hZGEgcG9yICRcYmV0YV8xJCwgJFxiZXRhXzIkCnkgJFxiZXRhXzQkLgoKJEVfbShwZXNvKSA9IChcYmV0YV8wICsgXGJldGFfMykgKyBcYmV0YV8xIFwqIGFsdHVyYSArIChcYmV0YV8yICsgXGJldGFfMiwzKSAqIGVkYWQgKyBcIFxiZXRhXzQgKiBjb25zdW1vU2VtYW5hbFNuYWNrcyQKCkVsIGdlbmVybyBtYXNjdWxpbm8gdGllbmUgdW5hIG9yZGVuYWRhIHF1ZSBlcyBsYSBzdW1hIGRlIGxhIG9yZGVuYWRhIGRlbCBnZW5lcm8gZmVtZW5pbm8gJFxiZXRhXzAkCm1hcyAkXGJldGFfMyQuIEx1ZWdvIGNhbWJpYSBsYSBwZW5kaWVudGUgJFxiZXRhXzIkIGRlIGxhIGVkYWQsIGEgbGEgY3VhbCBzZSBsZSBzdW1hICRcYmV0YV8yLDMkCgpMdWVnbywgc2FiaWVuZG8gcXVlIHNvbG8gY2FtYmlhbiBsb3MgY29lZmljaWVudGVzIGNvcnJlc3BvbmRpZW50ZXMgYWwgZ2VuZXJvIHkgZWRhZCwgc2kgbWFudGVuZW1vcwpjb250YW50ZXMgbG9zIGRlbcOhcyBjb2VmaWNpZW50ZXMgb2J0ZW5lbW9zOgoKLSAgICRFX2YocGVzbykgPSBcYmV0YV8wICsgXGJldGFfMiAqIGVkYWQgKyBjdGUkCi0gICAkRV9tKHBlc28pID0gKFxiZXRhXzAgKyBcYmV0YV8zKSArIChcYmV0YV8yICsgXGJldGFfMiwzKSAqIGVkYWQkCgpBaG9yYSByZWVtcGxhemFtb3MgcG9yIGxvcyBjb2VmaWNpZW50ZXMgcG9yIGxvIHZhbG9yZXMgcXVlIGVuY29udHLDsyBlbCBtb2RlbG86CgotICAgRmVtZW5pbm86CgogICAgLSAgICRFX2YocGVzbykgPSAtNjUuNTY0NTYxMDkgKyAxLjIyNTM5MDAyIGVkYWQgKyBjdGUkCgotICAgTWFzY3VsaW5vOgoKICAgIDEuICAkRV9tKHBlc28pID0gKC02NS41NjQ1NjEwOSArIC00LjYwNDY0NjMxKSArICgxLjIyNTM5MDAyICsgMC4zODkyNzU2NykgKiBlZGFkICsgY3RlJAogICAgMi4gICRFX20ocGVzbykgPS03MC4xNjkyMDc0ICsgMS42MTQ2NjU2OSAqIGVkYWQgKyBjdGUkCgpGaW5hbG1lbnRlLCBncmFmaWNhbW9zIGFtYmFzIHJlY3RhcyBkZWZpbmllbmRvIGxhICRjdGUkIGNvbiB1biB2YWxvciBxdWUgZGUgcGVzb3MgcG9zaXRpdm9zIHBhcmEKdGVuZXIgdW5hIGdyw6FmaWNhIGNvbnNpc3RlbnRlOgoKYGBge3IgZmlnLmhlaWdodD0zLCBmaWcud2lkdGg9Niwgd2FybmluZz1GQUxTRSwgZmlnLmFsaWduPSdjZW50ZXInfQpjdGUgPSAxMDAKCnRyYWluX3NldCAlPiUgCiAgbXV0YXRlKAogICAgcGVzbyA9IGlmZWxzZSgKICAgICAgZ2VuZXJvPT0nRmVtZW5pbm8nLCAKICAgICAgKC02NS41NjQ1NjEwOSArIDEuMjI1MzkwMDIgKiBlZGFkKSArIGN0ZSwKICAgICAgKC03MC4xNjkyMDc0ICsgMS42MTQ2NjU2OSAqIGVkYWQpICsgY3RlCiAgICApIAogICkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gZWRhZCwgeSA9IHBlc28sIGNvbG91cj1nZW5lcm8pKSArCiAgZ2VvbV9saW5lKCkgKwogIHlsYWIoJ1Blc28nKSArCiAgeGxhYignRWRhZCcpCmBgYAoKRmluYWxtZW50ZSwgc2UgcHVlZGUgYXByZWNpYXIgcXVlIGxhcyBvcmRlbmFkYXMgZGUgYW1ib3MgZ8OpbmVyb3Mgc29uIGRpc3RpbnRhcywgZG9uZGUgZWwgZ2VuZXJvCmZlbWVuaW5vIGluaWNpYSBkZXNkZSB1biBwZXNvIG1lbm9yIGFsIG1hc2N1bGluby4gTHVlZ28gc2kgdmFyaWFtb3Mgw7puaWNhbWVudGUgbGEgZWRhZCwgc2UgYXByZWNpYQpxdWUgZWwgcGVzbyBkZWwgZ2VuZXJvIG1hc2N1bGlubyBlcyBtYXlvciBhbCBmZW1lbmlubyBwYXJhIGxhIG1pc21hIGVkYWQgZW4gdG9kbyBsb3MgY2Fzb3MuIEVzdG8gc2UKZGViZSBhIHF1ZSBsYSByZXN0YSBjb3JyZXNwb25kaWVudGUgYWwgZ2VuZXJvIG1hc2N1bGluYSBlc3RhIHBvciBhcnJpYmEgZGUgbGEgcmVzdGEgY29ycmVzcG9uZGllbnRlCmFsIGdlbmVybyBmZW1lbmluby4KCiMjIyAzLjMuIMK/UXXDqSBwb3JjZW50YWplIGRlIGxhIHZhcmlhYmlsaWRhZCBleHBsaWNhIGVsIG1vZGVsbz8gRW4gY2FzbyBkZSBkZXRlY3RhciBxdWUgZXhpc3RlbiBjYXRlZ29yw61hcwoKbm8gc2lnbmlmaWNhdGl2YXMgZGUgbGEgdmFyaWFibGUgY29uc3Vtb19zZW1hbmFsX3NuYWNrcyBldmFsdWFyIHNpIGxhIHZhcmlhYmxlIGVzIHNpZ25pZmljYXRpdmEgZW4Kc3UgY29uanVudG8geSwgZW4gY2FzbyBhZmlybWF0aXZvLCBwcm9wb25lciB1bmEgcmVkZWZpbmljacOzbiBkZSBsYXMgbWlzbWFzIHF1ZSBwZXJtaXRhIG9idGVuZXIgdW5hCm1heW9yIHByb3BvcmNpw7NuIGRlIGNhdGVnb3LDrWFzIHNpZ25pZmljYXRpdmFzIGluZGl2aWR1YWxtZW50ZS4gTHVlZ28sIGFuYWxpemFyIHNpIGV4aXN0ZW4gY2FtYmlvcyBlbgpsYSB2YXJpYWJpbGlkYWQgZXhwbGljYWRhIHBvciBlbCBtb2RlbG8uCgpWaWVuZG8gZWwgcmVzdWx0YWRvIGRlICoqY29lZmZpY2llbnRzX3N1bW1hcnkqKiBzZSBhcHJlY2lhIHF1ZSBsYXMgc2lndWllbnRlcyBjYXRlZ29yw61hcyBkZQoqKmNvbnN1bW9fc2VtYW5hbF9zbmFja3MqKiBubyBzb24gc2lnbmlmaWNhdGl2YXM6CgotICAgMiB2ZWNlcyBhbCBkw61hICgxNCB2ZWNlcy9zZW1hbmEpCi0gICA0IGEgNiB2ZWNlcyBkdXJhbnRlIGxvcyDDumx0aW1vcyA3ICg0IGEgNiB2ZWNlcyBzZW1hbmEpCi0gICAzIHZlY2VzIGFsIGTDrWEgKDIxIHZlY2VzL3NlbWFuYSkKClBlcm8gc2kgc29uIHNpZ25pZmljYXRpdmFzIGxvcyBleHRyZW1vczoKCi0gICBEZSAxIGEgMyB2ZWNlcy9zZW1hbmEKLSAgIERlIDI4IG8gbWFzIHZlY2VzL3NlbWFuYQoKQSBjb250aW51YWNpw7NuIHNlIHJlYWxpemEgdW4gJEYkIHRlc3QgcGFyYSBldmFsdWFyIGxhIHNpZ25pZmljYXRpdmlkYWQgY29uanVudGEgZGUgbGFzIGNhdGVnw7NyaWNhcwpkZSBsYSB2YXJpYWJsZSAqKmNvbnN1bW9fc2VtYW5hbF9zbmFja3MqKiBwYXJhIGV4cGxpY2FyIGVsIHBlc28uCgpFbCAkRiQgdGVzdCB0YW1iacOpbiBsbGFtYW5kbyBBTk9WQSAoQW7DoWxpc2lzIGRlIGxhIHZhcmlhbnphKSBzZSByZWFsaXphIHBhcmEgcHJvYmFyIGxhCnNpZ25pZmljYXRpdmlkYWQgY29uanVudGEgZGUgdG9kb3MgbG9zIHZhbG9yZXMgZGUgdW5hIHZhcmlhYmxlIGNhdGVnw7NyaWNhLgoKTGFzIGhpcMOzdGVzaXMgc29uIGxhcyBzaWd1aWVudGVzOgoKLSAgICRIXzA6IM6yX3EgPSDOsl97cSsxfSA9IMK3IMK3IMK3ID0gzrJfe3DiiJIxfSA9IDAkCi0gICAkSF8xOiQgcG9yIGxvIG1lbm9zIHVubyBkZSBsb3MgJM6yX2skIChjb24gJGskIGVudHJlICRxJCB5ICRw4oiSMSQpIGVzIHRhbCBxdWUgJM6yX2sgXG5lcSAwJC4KCkx1ZWdvIHNpIHRvZG9zIGxvcyBjb2VmaWNpZW50ZXMgYXNvY2lhZG9zIGEgbG9zIHZhbG9yZXMgZGUgdmFyaWFibGUgY2F0ZWfDs3JpY2Egc29uIGNlcm8sIHNlIHJlY2hhemEKbGEgaGlww7N0ZXNpcyBudWxhIHkgcG9yIGxvIHRhbnRvIGxhIHZhcmlhYmxlIG5vIGVzIHNpZ25pZmljYXJ0aXZhIHBhcmEgZXhwbGljYXIgZWwgcGVzbyBlbiBudWVzdHJvCmNhc28uCgpBIGNvbnRpbnVhY2nDs24gdmVyZW1vcyBlbCBwLXZhbG9yIHJlc3VsdGFkbyBkZSBhcGxpY2FyICRGJCB0ZXN0IHBhcmEgY2FkYSB2YXJpYWJsZSBkZWwgbW9kZWxvOgoKYGBge3J9CmFub3ZhX3N1bW1hcnkobW9kZWxfMikKYGBgCgpQb2RlbW9zIGFwcmVjaWFyIHF1ZSBlbCAkcC12YWx1ZSA8IDAuMDA1JCBwYXJhIGxhIHZhcmlhYmxlICoqY29uc3Vtb19zZW1hbmFsX3NuYWNrcyoqLiBQb3IgbG8gdGFudG8Kc2UgcmVjaGF6YSBsYSBoaXDDs3Rlc2lzIG51bGEgeSBwb2RlbW9zIGRlY2lyIGVuIHN1IGNvbmp1bnRvIHJlc3VsdGEgZXN0YWTDrXN0aWNhbWVudGUgc2lnbmlmaWNhdGl2YQpwYXJhIGV4cGxpY2FyIGVsIHBlc28uIEx1ZWdvLCBjb21vIGxhIHZhcmlhYmxlICoqY29uc3Vtb19zZW1hbmFsX3NuYWNrcyoqIGVzIHNpZ25pZmljYXRpdmEgdmFsZSBsYQpwZW5hIHJlLWRlZmluaXJsYS4gUG9yIG90cm8gbGFkbywgbGEgY29tYmluYWNpw7NuIGRlIHZhcmlhYmxlcyBnZW5lcm8tZWRhZCBubyBlcyBlc3RhZMOtc3RpY2FtZW50ZQpzaWduaWZpY2F0aXZhIHBhcmEgZXhwbGljYXIgZWwgcGVzbywgcGVybyBzaSBsbyBlcyBlbCBnZW5lcm8gZW4gZm9ybWEgc2VwYXJhZGEuIEZpbmFsbWVudGUsIGNvbW8geWEKdmltb3MgZW4gcGFzb3MgYW50ZXJpb3JlcywgZWRhZCB5IGFsdHVyYSBzb24gc2lnbmlmaWNhdGl2YXMuCgoqKk1vZGVsbyAyOiBSZWRlZmluaWNpw7NuIDEqKgoKRGFkbyBxdWUgbm8gdG9kYXMgbGFzIGNhdGVnb3LDrWFzIGRlIGxhIHZhcmlhYmxlICoqY29uc3Vtb19zZW1hbmFsX3NuYWNrcyoqIHNpbiBzaWduaWZpY2F0aXZhcyBhCmNvbnRpbnVhY2nDs24gc2UgcHJvcG9uZSB1bmEgcmUtZGVmaW5pY2nDs24gZGUgc3VzIGNhdGVnb3LDrWFzIHF1ZSBoYWNlIHF1ZSB0b2RhcyBlbGxhcyBzZWFuCnNpZ25pZmljYXRpdmFzIHBhcmEgZWwgKiptb2RlbG8gMioqLgoKYGBge3J9CnRyYWluX3NldF9zbmFja18xIDwtIHRyYWluX3NldCAlPiUgbXV0YXRlKGNvbnN1bW9fc2VtYW5hbF9zbmFja3MgPSBjYXNlX3doZW4oCiAgY29uc3Vtb19zZW1hbmFsX3NuYWNrcyAlaW4lIGMoJzw9MycsICc0LTYnICwgJzcnKSAgfiAnPD03JywKICBjb25zdW1vX3NlbWFuYWxfc25hY2tzICVpbiUgYygnMTQnLCAnMjEnLCAnPj0yOCcpICB+ICc+PTE0JywKICBUUlVFIH4gYXMuY2hhcmFjdGVyKGNvbnN1bW9fc2VtYW5hbF9zbmFja3MpCikpCnRyYWluX3NldF9zbmFja18xJGNvbnN1bW9fc2VtYW5hbF9zbmFja3MgPC0gZmFjdG9yKAogIHRyYWluX3NldF9zbmFja18xJGNvbnN1bW9fc2VtYW5hbF9zbmFja3MsCiAgbGV2ZWxzPWMoJzAnLCAnPD03JywgJz49MTQnKSwgCiAgb3JkZXJlZD1GQUxTRQopCnRhYmxlKHRyYWluX3NldF9zbmFja18xJGNvbnN1bW9fc2VtYW5hbF9zbmFja3MpIApgYGAKCmBgYHtyfQptb2RlbF8yX3JlZGVmaW5pY2lvbl8xIDwtIGxtKAogIHBlc28gfiBhbHR1cmEgKyBlZGFkICsgZ2VuZXJvICsgY29uc3Vtb19zZW1hbmFsX3NuYWNrcyArICBnZW5lcm8gKiBlZGFkLCAKICBkYXRhID0gdHJhaW5fc2V0X3NuYWNrXzEKKQoKY29lZmZpY2llbnRzX3N1bW1hcnkobW9kZWxfMl9yZWRlZmluaWNpb25fMSkKYW5vdmFfc3VtbWFyeShtb2RlbF8yX3JlZGVmaW5pY2lvbl8xKQpnbGFuY2UobW9kZWxfMl9yZWRlZmluaWNpb25fMSkKYGBgCgoqKk1vZGVsbyAyOiBSZWRlZmluaWNpw7NuIDIqKgoKRW4gZXN0ZSBjYXNvIHNlIHByb3BvbmUgY2FsY3VsYXIgbGEgbWVkaWEgZGVsIHJhdGlvICoqYWx0dXJhL2VkYWQqKiBwYXJhIGNhZGEgY2F0ZWdvcsOtYSBkZSBsYQp2YXJpYWJsZXMgKipjb25zdW1vX3NlbWFuYWxfc25hY2tzKiouIEx1ZWdvIGNhbGN1bGFtb3MgbG9zIGN1YW50aWxlcyBkZSBlc3RhIG51ZXZhIGRpc3RyaWJ1Y2nDs24geQpsb3MgdXRpbGl6YW1vcyBwYXJhIGNyZWFyIHVuYSBudWV2YSBjYXRlZ29yaXphY2nDs246IGxvcyBpbmRpdmlkdW9zIHF1ZSB0ZW5nYSB1biByYXRpbyBtZW5vciBhbApjdWFudGlsIDIgdGVuZHJhbiBlbCB2YWxvciAqKkJham8qKiB5ICoqQWx0byoqIGVuIGNhc28gY29udHJhcmlvLiBTZSBpbnRlbnRvIGxsZXZhciBhIG1hcyBuaXZlbGVzCnBlcm8gZWwgdGVzdCAkVCQgbm8gZGFiYSBzaWduaWZpY2F0aXZvIHBhcmEgdG9kb3MgbG9zIGNvZWZpY2llbnRlcyBkZWwgKiptb2RlbG8gMioqLgoKMS4gIERlZmluaW1vcyBsYSBkaXN0cmljacOzbiBhbHR1cmEvZWRhZCBwb3IgY2F0ZWdvcmlhIGRlICoqY29uc3Vtb19zZW1hbmFsX3NuYWNrcyoqOgoKYGBge3IgZmlnLmhlaWdodD0yLCBmaWcud2lkdGg9NCwgd2FybmluZz1GQUxTRSwgZmlnLmFsaWduPSdjZW50ZXInfQp0cmFpbl9zZXRfc25hY2tfMiA8LSB0cmFpbl9zZXQgJT4lIAogIG11dGF0ZShhbHRfZWRhZF9yYXRpbyA9IHJvdW5kKGFsdHVyYS9lZGFkLCAwKSkKCmF2Z190cmFpbl9zZXRfc25hY2tfMiA8LSB0cmFpbl9zZXRfc25hY2tfMiAlPiUgCiAgIGdyb3VwX2J5KGNvbnN1bW9fc2VtYW5hbF9zbmFja3MpICU+JSAKICAgc3VtbWFyaXNlKGF2Z19hbHRfZWRhZF9yYXRpbyA9IG1lYW4oYWx0X2VkYWRfcmF0aW8pKQoKZ2dwbG90KGRhdGEgPSBhdmdfdHJhaW5fc2V0X3NuYWNrXzIsIGFlcyh4ID0gYXZnX2FsdF9lZGFkX3JhdGlvKSkgKyAKICBnZW9tX2JveHBsb3QoYWxwaGEgPSAwLjc1LCBmaWxsPSJibHVlIikgKwogIHRoZW1lX2J3KCkKYGBgCgpTZSBsb3Mgc2lndWllbnRlcyBjdWFudGlsZXMgdXRpbGl6YXJlbW9zIGVsIGN1YW50aWwgMig1MCUpOgoKYGBge3J9CnF1YW50aWxlc19hdmdfYWx0X2VkYWRfcmF0aW8gPC0gcXVhbnRpbGUoYXZnX3RyYWluX3NldF9zbmFja18yJGF2Z19hbHRfZWRhZF9yYXRpbykKcXVhbnRpbGVzX2F2Z19hbHRfZWRhZF9yYXRpbwpgYGAKCjIuICBDcmVhbW9zIHVuIGRhdGFzZXQgaW50ZXJtZWRpbyBkb25kZSBlc3RhbiBtYXBlYWRhcyBsYXMgY2F0ZWdvcmlhcyBvcmlnaW5hbGVzIHZzIGxhcyBudWV2YXM6CgpgYGB7cn0KcTIgPC0gcXVhbnRpbGVzX2F2Z19hbHRfZWRhZF9yYXRpb1szXQoKc25hY2tfbGV2ZWxfbWFwcGluZyA8LSBhdmdfdHJhaW5fc2V0X3NuYWNrXzIgJT4lIAogIG11dGF0ZShsZXZlbCA9IGNhc2Vfd2hlbigKICAgIGF2Z19hbHRfZWRhZF9yYXRpbyA8IHEyICB+ICdCYWpvJywKICAgIGF2Z19hbHRfZWRhZF9yYXRpbyA+PSAgcTIgfiAnQWx0bycKICApKSAlPiUgc2VsZWN0KGNvbnN1bW9fc2VtYW5hbF9zbmFja3MsIGxldmVsKQoKc25hY2tfbGV2ZWxfbWFwcGluZyAlPiUKICBhcnJhbmdlKGNvbnN1bW9fc2VtYW5hbF9zbmFja3MpCmBgYAoKMy4gIENyZWFtb3MgbnVldm9zIGRhdGFzZXQgZGUgdHJhc2luL3Rlc3QgY29uIGxhIG51ZXZhIGRlZmluaWNpw7NuIGRlIGxhIHZhcmlhYmxlCiAgICAqKmNvbnN1bW9fc2VtYW5hbF9zbmFja3MqKjoKCmBgYHtyIGZpZy5oZWlnaHQ9NCwgZmlnLndpZHRoPTUsIHdhcm5pbmc9RkFMU0UsIGZpZy5hbGlnbj0nY2VudGVyJ30KdHJhaW5fc2V0X3NuYWNrXzIgPC0gdHJhaW5fc2V0ICU+JQogIGlubmVyX2pvaW4oc25hY2tfbGV2ZWxfbWFwcGluZywgYnkgPSAnY29uc3Vtb19zZW1hbmFsX3NuYWNrcycpICU+JQogIG11dGF0ZShjb25zdW1vX3NlbWFuYWxfc25hY2tzID0gbGV2ZWwpICU+JSAKICBzZWxlY3QoLWxldmVsKQoKdGVzdF9zZXRfc25hY2tfMiA8LSB0ZXN0X3NldCAlPiUKICBpbm5lcl9qb2luKHNuYWNrX2xldmVsX21hcHBpbmcsIGJ5ID0gJ2NvbnN1bW9fc2VtYW5hbF9zbmFja3MnKSAlPiUKICBtdXRhdGUoY29uc3Vtb19zZW1hbmFsX3NuYWNrcyA9IGxldmVsKSAlPiUgCiAgc2VsZWN0KC1sZXZlbCkKYGBgCgpgYGB7ciB3YXJuaW5nPUZBTFNFfQp0cmFpbl9zZXRfc25hY2tfMiAlPiUgIAogIGdyb3VwX2J5KGNvbnN1bW9fc2VtYW5hbF9zbmFja3MpICU+JSAKICB0YWxseSgpCgp0ZXN0X3NldF9zbmFja18yICU+JSAgCiAgZ3JvdXBfYnkoY29uc3Vtb19zZW1hbmFsX3NuYWNrcykgJT4lIAogIHRhbGx5KCkKYGBgCgo0LiAgRXZhbHVhbW9zIGVsIG51ZXZvIHRyYWluX3NldCBjb24gZWwgKiptb2RlbG8gMioqOgoKYGBge3J9Cm1vZGVsXzJfcmVkZWZpbmljaW9uXzIgPC0gbG0oCiAgcGVzbyB+IGFsdHVyYSArIGVkYWQgKyBnZW5lcm8gKyBjb25zdW1vX3NlbWFuYWxfc25hY2tzICsgIGdlbmVybyAqIGVkYWQsIAogIGRhdGEgPSB0cmFpbl9zZXRfc25hY2tfMgopCgpjb2VmZmljaWVudHNfc3VtbWFyeShtb2RlbF8yX3JlZGVmaW5pY2lvbl8yKQphbm92YV9zdW1tYXJ5KG1vZGVsXzJfcmVkZWZpbmljaW9uXzIpCmdsYW5jZShtb2RlbF8yX3JlZGVmaW5pY2lvbl8yKQpgYGAKCmBgYHtyIGZpZy5oZWlnaHQ9MywgZmlnLndpZHRoPTYsIHdhcm5pbmc9RkFMU0UsIGZpZy5hbGlnbj0nY2VudGVyJ30KbW9kZWxzIDwtIGxpc3QoCiAgJ01vZGVsbyAxJyA9IG1vZGVsXzEsIAogICdNb2RlbG8gMicgPSBtb2RlbF8yLCAKICAnTW9kZWxvIDIgLSBSZS1kZWZpbmljacOzbiAxJyA9IG1vZGVsXzJfcmVkZWZpbmljaW9uXzEsIAogICdNb2RlbG8gMiAtIFJlLWRlZmluaWNpw7NuIDInID0gbW9kZWxfMl9yZWRlZmluaWNpb25fMgopCgptb2RlbHMgJT4lIAogIG1hcF9kZihnbGFuY2UsIC5pZCA9ICJtb2RlbCIpICU+JQogIGFycmFuZ2UoZGVzYyhhZGouci5zcXVhcmVkKSkKYGBgCgoqKkNvbmNsdXNpw7NuKio6IEFtYm9zIG1vZGVsb3Mgc29uIHNpZ25pZmljYXRpdm9zIHBhcmEgZXhwbGljYXIgZWwgcGVzby4gRWwgbW9kZWxvICoqTW9kZWxvIDIgLQpSZS1kZWZpbmljacOzbiAxKiogZXMgbWFzIGV4cGxpY2F0aXZvLCB5YSBxdWUgJFJeMiQgYWp1c3RhZG8gZXMgbWF5b3IuIEZpbmFtZW50ZSwgYW1ib3MgbW9kZWxvcyBzb24KbWVubyBleHBsaWNhdGl2b3MgcXVlIGVsIG1vZGVsbyBvcmlnaW5hbCgqKk1vZGVsbyAyKiopLgoKIyMgNC4gTW9kZWxvcyBwcm9waW9zIHkgZXZhbHVhY2nDs24KCiMjIyA0LjEuIFJlYWxpemFyIDIgbW9kZWxvcyBsaW5lYWxlcyBtw7psdGlwbGVzIGFkaWNpb25hbGVzIHkgZXhwbGljYXIgYnJldmUtbWVudGUgbGEgbMOzZ2ljYSBkZXRyw6FzIGRlIGxvcyBtaXNtb3MgKHNlIHZhbG9yYXLDoSBsYSBjcmVhY2nDs24geS9vIGluY2x1c2nDs24gZGUgdmFyaWFibGVzIG51ZXZhcykuIEV2YWx1YXIgbGEgcGVyZm9ybWFuY2UgZGVsIG1vZGVsbyBpbmljaWFsLCBlbCBtb2RlbG8gY2F0ZWfDs3JpY2FzIGNvbiBsYXMgY2F0ZWdvcsOtYXMgcmVkZWZpbmlkYXMgZGUgbGEgdmFyaWFibGUgY29uc3Vtb19zZW1hbmFsX3NuYWNrcyB5IGxvcyBtb2RlbG9zIGRlc2Fycm9sbGFkb3MgZW4gZXN0ZSBwdW50byBlbiBlbCBkYXRhc2V0IGRlIGVudHJlbmFtaWVudG8geSBldmFsdWFjacOzbiAodXNhciBkYXRhc2V0ICJlbmN1ZXN0YV9zYWx1ZF90ZXN0LmNzdiIpLiBMYSBldmFsdWFjacOzbiBkZSBwZXJmb3JtYW5jZSBjb25zaXN0ZSBlbiBjb21wYXJhciBlbiBhbWJvcyBzZXRzIGxhIHBlcmZvcm1hbmNlIGVuIHTDqXJtaW5vcyBkZWwgUiBjdWFkcmFkbyBhanVzdGFkbywgUk1TRSB5IE1BRS4KCkFsIGNvbnRpbnVhY2nDs24gc2UgZGVmaW5lIDIgbW9kZWxvcy4KCioqTW9kZWxvIDQqKgoKJEUocGVzbykgPSBcYmV0YV8wICsgXGJldGFfMSAqIGFsdHVyYSArIFxiZXRhXzIgKiBlZGFkICsgKyBcYmV0YV8zICogZ2VuZXJvICsgXGJldGE0ICogY29uc3Vtb1NlbWFuYWxTbmFja3MgKyBcYmV0YV81ICogZGlhc0FjdGl2aWRhZEZpc2ljYVNlbWFuYWwgKyBcYmV0YV82ICogYWx0dXJhICogZ2VuZXJvJAoKU2UgdXRpbGl6byBsYSByZWRlZmluaWNpw7NuIGRlIGxhIHZhcmlhYmxlICoqY29uc3Vtb19zZW1hbmFsX3NuYWNrcyoqIGNvbW8gYmFzZS4gQWRlbWFzZSBzZSBhZ3JlZ2FyCmxhIHZhcmlhYmxlIGRpYXNfYWN0aXZpZGFkX2Zpc2ljYV9zZW1hbmFsIGVudGVuZGllbmRvIHF1ZSB0aWVuZSB1bmEgaW5mbHVlbmNpYSBpcG9ydGFudGUgZW4gZWwgcGVzbwp5IGx1ZWdvIGxhIGFzb2NpYWNpb24gYWx0dXJhIFwqIGdlbmVybyB5YSBxdWUgZW4gZ2VuZXJhbCBtYXMgbXVqZXJlcyB0aWVuZW4gYSBzZXIgbWFzIGJhamFyIHF1ZSBsb3MKdmFyb25lcyB5IHZpc2UgdmVyc2EuCgpgYGB7ciBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD02LCB3YXJuaW5nPUZBTFNFLCBmaWcuYWxpZ249J2NlbnRlcid9Cm1vZGVsXzQgPC0gbG0oCiAgcGVzb34gCiAgICBhbHR1cmEgKyAKICAgIGVkYWQgKyAKICAgIGdlbmVybyArIAogICAgY29uc3Vtb19zZW1hbmFsX3NuYWNrcyArCiAgICBkaWFzX2FjdGl2aWRhZF9maXNpY2Ffc2VtYW5hbCArCiAgICBhbHR1cmEqZ2VuZXJvLAogIGRhdGEgPSB0cmFpbl9zZXRfc25hY2tfMQopCgpjb2VmZmljaWVudHNfc3VtbWFyeShtb2RlbF80KQphbm92YV9zdW1tYXJ5KG1vZGVsXzQpCmdsYW5jZShtb2RlbF80KQpgYGAKCmBgYHtyIGZpZy5oZWlnaHQ9MywgZmlnLndpZHRoPTYsIHdhcm5pbmc9RkFMU0UsIGZpZy5hbGlnbj0nY2VudGVyJ30KdHJhaW5fc2V0MyA8LSBjb2x1bW5fbWVhbl9xdWFudGlsZV9iaW5uaW5nKHRyYWluX3NldF9zbmFja18xLCAnZGlhc19hY3RpdmlkYWRfZmlzaWNhX3NlbWFuYWwnKQp0ZXN0X3NldDMgIDwtIGNvbHVtbl9tZWFuX3F1YW50aWxlX2Jpbm5pbmcodHJhaW5fc2V0X3NuYWNrXzEsICAnZGlhc19hY3RpdmlkYWRfZmlzaWNhX3NlbWFuYWwnKQoKdHJhaW5fc2V0MyA8LSBjb2x1bW5fbWVhbl9xdWFudGlsZV9iaW5uaW5nKHRyYWluX3NldDMsICdjb25zdW1vX3NlbWFuYWxfZnJ1dGFzJykKdGVzdF9zZXQzICA8LSBjb2x1bW5fbWVhbl9xdWFudGlsZV9iaW5uaW5nKHRlc3Rfc2V0MywgICdjb25zdW1vX3NlbWFuYWxfZnJ1dGFzJykKCnRyYWluX3NldDMgPC0gY29sdW1uX21lYW5fcXVhbnRpbGVfYmlubmluZyh0cmFpbl9zZXQzLCAnY29uc3Vtb19zZW1hbmFsX3ZlcmR1cmEnKQp0ZXN0X3NldDMgIDwtIGNvbHVtbl9tZWFuX3F1YW50aWxlX2Jpbm5pbmcodGVzdF9zZXQzLCAgJ2NvbnN1bW9fc2VtYW5hbF92ZXJkdXJhJykKCnRyYWluX3NldDMgPC0gY29sdW1uX21lYW5fcXVhbnRpbGVfYmlubmluZyh0cmFpbl9zZXQzLCAnY29uc3Vtb19zZW1hbmFsX2NvbWlkYV9ncmFzYScpCnRlc3Rfc2V0MyAgPC0gY29sdW1uX21lYW5fcXVhbnRpbGVfYmlubmluZyh0ZXN0X3NldDMsICAnY29uc3Vtb19zZW1hbmFsX2NvbWlkYV9ncmFzYScpCgp0cmFpbl9zZXQzIDwtIGNvbHVtbl9tZWFuX3F1YW50aWxlX2Jpbm5pbmcodHJhaW5fc2V0MywgJ2NvbnN1bW9fc2VtYW5hbF9nYXNlb3NhcycpCnRlc3Rfc2V0MyAgPC0gY29sdW1uX21lYW5fcXVhbnRpbGVfYmlubmluZyh0ZXN0X3NldDMsICAnY29uc3Vtb19zZW1hbmFsX2dhc2Vvc2FzJykKCnNlZ21lbnRlZF9ib3hfcGxvdCgKICB0ZXN0X3NldDMsIAogIGNvbHVtbiAgICAgICAgPSAncGVzbycsIAogIHNlZ21lbnRlZF9ieSAgPSAnZGlhc19hY3RpdmlkYWRfZmlzaWNhX3NlbWFuYWwnLAogIHRpdGxlICAgICAgICAgPSAnTml2ZWxlcyBhY3RpdmlkYWQgZmlzaWNhIG9yZGVuYWRvcyBwb3IgbGEgbWVkaWFuYSBkZWwgcGVzbyBlbiBUZXN0JywKICB5X2xhYmVsICAgICAgID0gJ1Blc28gKEtnKScsCiAgeV9saW1pdHMgICAgICA9IGMoNDAsIDEwMCksCiAgeF9sYWJlbCAgICAgICA9ICdOaXZlbGVzIGRlIGFjdGl2aWRhZCBmw61zaWNhIChEaWFzKScKKQpgYGAKCioqTW9kZWxvIDUqKgoKJEUocGVzbykgPSBcYmV0YV8wICsgXGJldGFfMSAqIGFsdHVyYSArIFxiZXRhXzIgKiBlZGFkICsgKyBcYmV0YV8zICogZ2VuZXJvICsgXGJldGE0ICogY29uc3Vtb1NlbWFuYWxTbmFja3MgKyBcXCBcYmV0YV81ICogZGlhc0FjdGl2aWRhZEZpc2ljYVNlbWFuYWwgKyBcYmV0YV82ICogY29uc3Vtb1NlbWFuYWxGcnV0YXMgKyBcYmV0YV83ICogY29uc3Vtb1NlbWFuYWxWZXJkdXJhcyArIFxcKiBcYmV0YV84ICogY29uc3Vtb1NlbWFuYWxHcmFzYXMgKyBcYmV0YV85ICogY29uc3Vtb1NlbWFuYWxHYXNlb3NhcyQKClNlIHV0aWxpem8gbGEgcmVkZWZpbmljacOzbiBkZSBsYSB2YXJpYWJsZSAqKmNvbnN1bW9fc2VtYW5hbF9zbmFja3MqKiBjb21vIGJhc2UuIEFkZW1hc2Ugc2UgYWdyZWdhcgpsYSB2YXJpYWJsZSBjb25zdW1vX3NlbWVuYWxfZnJ1dHJhcy92ZXJkdXJhcy9ncmFzYXMvZ2FzZWFvc2FzIGVudGVuZGllbmRvIHF1ZSB0YW1iacOpbiB0aWVuZSB1bmEKaW5mbHVlbmNpYSBpbXBvcnRhbnRlIGVuIGVsIHBlc28uCgpgYGB7ciBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD02LCB3YXJuaW5nPUZBTFNFLCBmaWcuYWxpZ249J2NlbnRlcid9Cm1vZGVsXzUgPC0gbG0oCiAgcGVzbyB+IAogIGVkYWQgKwogIGdlbmVybyArCiAgYWx0dXJhICsKICBjb25zdW1vX3NlbWFuYWxfc25hY2tzICsKICBjb25zdW1vX3NlbWFuYWxfZnJ1dGFzICsgCiAgY29uc3Vtb19zZW1hbmFsX3ZlcmR1cmEsCiAgZGF0YSA9IHRyYWluX3NldDMKKQoKY29lZmZpY2llbnRzX3N1bW1hcnkobW9kZWxfNSkKYW5vdmFfc3VtbWFyeShtb2RlbF81KQpnbGFuY2UobW9kZWxfNSkKYGBgCgpgYGB7cn0KYyhtb2RlbHMsIGxpc3QoJ01vZGVsbyA0Jz1tb2RlbF80LCAnTW9kZWxvIDUnPW1vZGVsXzUpKSAlPiUgCiAgbWFwX2RmKGdsYW5jZSwgLmlkID0gIm1vZGVsIikgJT4lCiAgYXJyYW5nZShkZXNjKGFkai5yLnNxdWFyZWQpKQpgYGAKCkZpbmFsbWVudGUsIHNpIGNvbXBhcmFtb3MgbG9zIG1vZGVsb3MgcG9yICRSXjIkIEFqdXN0YWRvLCBzZSBwdWVkZSBhcHJlY2lhciBxdWUgZWwgbW9kZWxvIDUgKGNvbgp0b2RhcyBsYXMgdmFyaWFibGVzIGNhdGVnw7NyaWNhcyByZS1kZWZpbmlkYXMpIGxsZWdhIGEgY2FwdGFyIGxhIG1heW9yIHZhcmlhbnphIGV4cGxpY2FkYSBzb2JyZSBlbApkYXRhc2V0IGRlIGVudHJlbmFtaWVudG8uIFBvciBzdXB1ZXN0byBlc3RvIG5vIGRpY2UgbmFkYSBhY2VyY2EgZGUgbGEgcGVyZm9ybWFuY2UgZGVsIG1vZGVsbyBlbgp0ZXN0LCBwZXJvIHNpIHF1ZSB0aWVuZSBsYSBtZWpvciBjYXBhY2lkYWQgcGFyYSBleHRyYWVyIGluZm9ybWFjacOzbiBkZSBsb3MgZGF0byBkZSBlbnRyZW5hbWllbnRvLgoKIyMjIDQuMi4gwr9DdcOhbCBlcyBlbCBtZWpvciBtb2RlbG8gcGFyYSBudWVzdHJvIG9iamV0aXZvIGRlIHByZWRlY2lyIGVsIHBlc28/IMK/UG9yIHF1w6k/CgpBaG9yYSBjb21wYXJhbW9zIGxhIHBlcmZvcm1hbmNlIGRlIHRvZG8gbG9zIG1vZGVsb3MgYWwgZXZhbHVhciBlbCBlcnJvciBkZWxvcyBtaXNtbyBhbCBwcmVkZWNpciBlbApwZXNvIGVuIGVsIGNvbmp1bnRvIGRlIHRyYWluIHkgdGVzdCB0YW50byBwYXJhIFJNU0UgY29tbyBNQUU6CgoqKlJNU0UqKgoKYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCB3YXJuaW5nPUZBTFNFfQpjdXN0b21fbW9kZWxzX2V2YWx1YXRpb25fc3VtbWFyeSgKICBtb2RlbF8xLCBtb2RlbF8yLCBtb2RlbF8yX3JlZGVmaW5pY2lvbl8xLCBtb2RlbF80LCBtb2RlbF81LAogIHRlc3Rfc2V0LCB0cmFpbl9zZXRfc25hY2tfMSwgdGVzdF9zZXQzLAogIG1ldHJpY19mbiA9IHJtc2UKKQpgYGAKClNpIHV0aWxpemFtb3MgbGEgbcOpdHJpY2EgUk1TRSBwb2RlbW9zIHZlciBxdWUgZWwgbW9kZWxvIDUgdGllbmUgZWwgbWVub3IgZXJyb3IgZW4gZWwgY29uanVudG8gZGUKdGVzdC4gUG9yIG90cm8gbGFkb3MgZWwgcXVlIHRpZW5lIGxhIG1heW9yIGRpZmVyZW5jaWEgZGUgZXJyb3IgZW50cmUgdGVzdCB5IGVudHJlbmFtaWVudG8uIEVzdG8gbm9zCmRpY2UgcXVlIHBvZHLDrWEgZXN0YXIgc29icmUtYWp1c3RhbmRvc2UgYWwgY29uanVudG8gZGUgZW50cmVuYW1pZW50by4gRWwgbW9kZWxvIDMgdGllbmUgdW4gZXJyb3IgZW4KdGVzdCBtdXkgY2VyY2FubyB5IGFkZW1hcyB0aWVuZSB1biBkaWZlcmVuY2lhIGVudHJlIHRlc3QgeSB0cmFpbiBtdWNobyBtZW5vci4gcG9yIGVzdG8gdWx0aW1vIHBhcmVjZQpzZXIgZWwgbWVqb3IgbW9kZWxvIHlhIHF1ZSB0aWVuZSBwcsOhY3RpY2FtZW50ZSBlbCBtZW5vciBlcnJvciBwb3NpYmxlIHkgdGFtYmnDqW4gZWwgbWVub3IKc29icmUtYWp1c3RlIGFsIGNvbmp1bnRvIGRlIGVudHJlbmFtaWVudG8uCgoqKk1BRSoqCgpgYGB7ciB3YXJuaW5nPUZBTFNFfQpjdXN0b21fbW9kZWxzX2V2YWx1YXRpb25fc3VtbWFyeSgKICBtb2RlbF8xLCBtb2RlbF8yLCBtb2RlbF8yX3JlZGVmaW5pY2lvbl8xLCBtb2RlbF80LCBtb2RlbF81LAogIHRlc3Rfc2V0LCB0cmFpbl9zZXRfc25hY2tfMSwgdGVzdF9zZXQzLAogIG1ldHJpY19mbiA9IG1hZQopCmBgYAoKU2kgbWVkaW1vcyBhIHBhcnRpciBkZWwgTUFFIHN1Y2VkZSBhbGdvIG11eSBzaW1pbGFyLCBFbCBtb2RlbG8gMyBlcyBlcyBxdWUgdGllbmUgbWVub3IgZXJyb3IgeQphZGVtYXMgbWVub3Mgc29icmUtYWp1c3RlLgoKRmluYWxtZW50ZSwgc2Vnw7puIGFtYmFzIG1ldHJpY2FzIGVsIG1vZWpvciBtb2RlbG8gZXMgZWwgKipNb2RlbG8gMyoqLgoKIyMgNS4gRGlhZ27Ds3N0aWNvIGRlbCBtb2RlbG8KCkFuYWxpemFyIGVuIHByb2Z1bmRpZGFkIGVsIGN1bXBsaW1pZW50byBkZSBsb3Mgc3VwdWVzdG9zIGRlbCBtb2RlbG8gbGluZWFsIHBhcmEgZWwgbW9kZWxvIGluaWNpYWwuCgpgYGB7ciBmaWcuaGVpZ2h0PTQsIGZpZy53aWR0aD02LCB3YXJuaW5nPUZBTFNFLCBmaWcuYWxpZ249J2NlbnRlcid9CnBsb3QobW9kZWxfMSkKYGBgCgoqKkhvbW9jZWRhc3RpY2lkYWQqKgoKQWwgdmlzdWFsaXphciBlbCBwcmltZXIgZ3LDoWZpY28gKFJlc2lkdW9zIHZzLiBWYWxvcmVzIGFqdXN0YWRvcykgc2UgcHVlZGUgYXByZWNpYXIgcXVlIGhheSBwcmVzZW5jaWEKZGUgaG9tb2NlZGFzdGljaWRhZCwgeWEgcXVlIGEgbWVkaWRhIHF1ZSBhdW1lbnRhbiBsb3MgdmFsb3JlcyBwcmVkaWNob3MgbGEgdmFyaWFiaWxpZGFkIG8gYW1wbGl0dWQKZGUgbG9zIHJlc2lkdW9zIHBhcmVjZSBtYW50ZW5lcnNlIGVuIGxvcyBtaXNtbyBuaXZlbGVzLiBEYWRhcyBlc3RhIGNvbmRpY2lvbmVzIHBvZGVtb3MgZGVjaXIgcXVlIHNlCmN1bXBsZSBlbCBzdXB1ZXN0byBkZSB2YXJpYW56YSBjb25zdGFudGUuCgoqKk5vcm1hbGlkYWQqKgoKQWwgdmlzdWFsaXphciBlbCBkaWFncmFtYSAqKlFRLVBsb3QqKiBwb2RlbW9zIG9ic2VydmFzIHF1ZSBlbiBlbCBleHRyZW1vIGRlcmVjaG8sIGVsIG1vZGVsbwpzb2JyZS1lc3RpbWEgZWwgcGVzbyBkZWwgbG9zIGluZGl2aWR1b3MgeWEgcXVlIGhheSB1bmEgZ3JhbiBkaWZlcmVuY2lhIGVudHJlIGxvcyB2YWxvcmVzIHByZWRpY2hvcyB5CmxvcyB2YWxvcmVzIGVzcGVyYWRvcyB0ZcOzcmljb3MuIExvIGNvbnRyYXJpbyBzdWNlZGUgYSBpenF1aWVyZGEsIGRvbmRlIGVsIG1vZGVsbyBzdWJlc3RpbWEgZWwgdmFsb3IKZGUgcGVzbyBlbiBjb21wYXJhY2nDs24gYWwgdmFsb3IgZXNwZXJhZG8gdGXDs3JpY28sIGF1bnF1ZSBsb3MgdmFsb3JlcyBkZSBsb3MgcmVzaWR1b3Mgc29uIG1lbm9yZXMgZW4KZXN0ZSBjYXNvLiBGaW5hbG1lbnRlIGVsIFFRLVBsb3QgbXVlc3RyYSB1biBncmFkbyBkZSBhbGVqYW1pZW50byBwcm9udW5jaWFkbyBkZSB1bmEgZGlzdHJpYnVjacOzbgpub3JtYWwgdGXDs3JpY2EgeSBwb3IgbG8gdGFudG8gbm8gc2UgY3VtcGxlIGVsIHN1cHVlc3RvIGRlIG5vcm1hbGlkYWQgZGVsIG1vZGVsby4KCioqQXBhbGFuY2FtaWVudG8gKExldmVyYWdlKSoqCgpTaSBvYnNlcnZhbW9zIGVsIGdyw6FmaWNvIGRlICoqUmVzaWR1b3MgdnMgQXBhbGFjYW1pZW50byoqIHZlbW9zIHZhcmlhcyBvYnNlcnZhY2lvbmVzIG8gaW5kaXZpZHVvcwpxdWUgc2UgYWxlamFuIGEgZGVyZWNoYSBkZWwgY3VtdWxvIHByaW5jaXBhbC4gRXN0b3MgZWplcmNlbiB1biBhbGVqYW1pZW50byBkZSBsYXMgcHJlZGljaW9uZXMgZGVsCm1vZGVsbyB2cyBsb3MgdmFsb3JlcyByZWFsZXMgYSBwYXJ0aXIgZGUgdW4gYXBhbGFuY2FtaWVudG8obGV2ZXJhZ2UpIDAuMDAyNSB5IGVzIG1hcyBwcm9udW5jaWFkbwpkZXNkZSAwLjAwMzUuIEZpbmFsbWVudGUsIHZlbW9zIHVuIGdyYWRvIGltcG9ydGFudGUgZGUgZGVzdmnDsyBkZSBsYXMgcHJlZGljY2lvbmVzIHZzIHZhbG9yZXMgcmVhbGVzCnkgcG9ybiBlbnRlIHVuIGdyYWRvIGltcG9ydGFudGUgZGUgYXBhbGFuY2FtaWVudG8obGV2ZXJhZ2UpLgoKQSBjb250aW51YWNpw7NuIHNlIHB1ZWRlbiB2ZXIgbG8gaW5kaXZpZHVvcyBxdWUgcHJvZHVjZW4gbWF5b3IgYXBhbGFuY2FtaWVudG8obGV2ZXJhZ2UpIHkgcG9yIGVuZGUKc2VzZ28gZW4gbGFzIHByZWRpY2Npb25lcyBkZWwgbW9kZWxvOgoKYGBge3J9CmF1Z21lbnQobW9kZWxfMSkgJT4lCiAgZmlsdGVyKC5oYXQ+MC4wMDI1KSAlPiUKICBhcnJhbmdlKC5oYXQpCmBgYAoKIyMgNi4gTW9kZWxvIFJvYnVzdG8KCkxlZXIgZWwgYXJjaGl2byAiZW5jdWVzdGFfc2FsdWRfbW9kZWxvNi5jc3YiLiBFc3RlIMO6bHRpbW8gY29uc2lzdGUgZW4gZWwgZGF0YXNldCBvcmlnaW5hbCBkZSB0cmFpbgpjb24gbGEgaW5jb3Jwb3JhY2nDs24gZGUgYWxndW5hcyBvYnNlcnZhY2lvbmVzIGFkaWNpb25hbGVzIHF1ZSBwdWVkZW4gaW5jbHVpciB2YWxvcmVzIGF0w61waWNvcy4gRW4KcGFydGljdWxhciwgb2JzZXJ2YXIgbGEgcmVsYWNpw7NuIGVudHJlIHBlc28geSBhbHR1cmEgwr9RdcOpIG9jdXJyZSBjb24gZXN0b3MgbnVldm9zIGRhdG9zPyBFbnRyZW5hciBlbAptb2RlbG8gaW5pY2lhbCBjb24gZXN0b3MgbnVldm9zIGRhdG9zIHkgY29tZW50YXIgcXXDqSBzZSBvYnNlcnZhIGVuIGxvcyBjb2VmaWNpZW50ZXMgZXN0aW1hZG9zIHkgbGFzCm3DqXRyaWNhcyBkZSBldmFsdWFjacOzbiAoUiBjdWFkcmFkbyBhanVzdGFkbywgUk1TRSB5IE1BRSkgcmVzcGVjdG8gYWwgbW9kZWxvIGVudHJlbmFkbyBjb24gZWwgc2V0IGRlCmVudHJlbmFtaWVudG8gb3JpZ2luYWwuIEVudHJlbmFyIHVuIG1vZGVsbyByb2J1c3RvIGNvbiBsYSBtaXNtYSBlc3BlY2lmaWNhY2nDs24gcXVlIGVsIG1vZGVsbyBpbmljaWFsCnNvYnJlIGxvcyBudWV2b3MgZGF0b3MuIENvbXBhcmFyIGxvcyBjb2VmaWNpZW50ZXMgeSBzdSBwZXJmb3JtYW5jZSAoUk1TRSB5IE1BRSkgcmVzcGVjdG8gYWwgbW9kZWxvCmluaWNpYWwgbm8gcm9idXN0byBlbnRyZW5hZG8gZW4gZXN0ZSBwdW50by4gwr9RdcOpIHB1ZWRlIGNvbmNsdWlyIGFsIHJlc3BlY3RvPwoKU2UgY2FyZ2EgZWwgY29uanVudG8gZGUgZW50cmVuYW1pZW50byBlbiBjcnVkbyxlIHMgZGVjaXIgc2luIHByZS1wcm9jZXNhbWllbnRvLiBMdWVnbyBzZSByZXN1bWVuIGxvcwp2YWxvcmVzIGRlIGxhcyB2YXJpYWJsZXMgY2F0ZWfDs3JpY2FzIHkgc2UgZWxpbWluYW4gbWlzc2luZyB2YWx1ZXMsIHlhIHF1ZSBzaWd1ZW4gc2llbmRvIG11eSBwb2NvCmNhc29zOgoKYGBge3Igd2FybmluZz1GQUxTRX0Kb3JpZ2luYWxfdHJhaW5fc2V0IDwtIHNob3J0ZW5fdmFsdWVzKHByZXByb2Nlc3MobG9hZF9vcmlnaW5hbF90cmFpbl9zZXQoKSkpCm1pc3NpbmdzX3N1bW1hcnkob3JpZ2luYWxfdHJhaW5fc2V0KQpuZXdfdHJhaW5fc2V0IDwtIHByb2Nlc3NfbWlzc2luZ3Mob3JpZ2luYWxfdHJhaW5fc2V0KQptaXNzaW5nc19zdW1tYXJ5KG5ld190cmFpbl9zZXQpCmBgYAoKYGBge3J9Cm5yb3cob3JpZ2luYWxfdHJhaW5fc2V0KQpucm93KG5ld190cmFpbl9zZXQpCmBgYAoKQ29tcGFyZW1vcyBsYXMgZGlzdHJpYnVjaW9uZXMgZGVsIHBlc28gdnMgYWx0dXJhIGVuIGFtYm9zIGNvbmp1bnRvIGRlIGVudHJlbmFtaWVudG86CgpgYGB7ciBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD02LCB3YXJuaW5nPUZBTFNFLCBmaWcuYWxpZ249J2NlbnRlcid9CmJveF9wbG90cygKICB0cmFpbl9zZXQgJT4lIHNlbGVjdChwZXNvLCBhbHR1cmEpLCAKICB0aXRsZSA9ICdDb21wYXJhdGl2YXMgZGUgZGlzdHJpYnVjaW9uZXMgZGVsIHBlc28geSBsYSBhbHR1cmEnCikKCmJveF9wbG90cygKICBuZXdfdHJhaW5fc2V0ICU+JSBzZWxlY3QocGVzbywgYWx0dXJhKSwgCiAgdGl0bGUgPSAnQ29tcGFyYXRpdmFzIGRlIGRpc3RyaWJ1Y2lvbmVzIGRlbCBwZXNvIHkgbGEgYWx0dXJhJwopCmBgYAoKRW4gZWwgZGF0YXNldCBkZSBlbnRyZW5hbWllbnRvIG9yaWdpbmFsIGxhIHZhcmlhYmxlIHBlc28gdGllbmUgcHLDoWN0aWNhbWVudGUgZWwgZG9ibGUgZGUgb3V0bGllcnMKcXVlIGVsIGRhdGFzZXQgcHJvY2VzYWRvLgoKKipNb2RlbG8gNioqCgpEZWZpbmltb3MgdW4gbW9kZWxvIGlndWFsIGFsICoqbW9kZWxvIDEqKiBwZXJvIGVudHJlbmFuZG8gZW4gZWwgZGF0YXNldCBkZSBlbnRyZW5hbWllbnRvIG9yaWdpbmFsLgoKYGBge3IgZmlnLmhlaWdodD0zLCBmaWcud2lkdGg9Niwgd2FybmluZz1GQUxTRSwgZmlnLmFsaWduPSdjZW50ZXInfQptb2RlbF82IDwtIGxtKAogIHBlc28gfiBhbHR1cmEgKyBlZGFkICsgZGlhc19hY3RpdmlkYWRfZmlzaWNhX3NlbWFuYWwgKyBjb25zdW1vX2RpYXJpb19hbGNvaG9sLCAKICBkYXRhID0gbmV3X3RyYWluX3NldAopCgpjb2VmZmljaWVudHNfc3VtbWFyeShtb2RlbF82KQphbm92YV9zdW1tYXJ5KG1vZGVsXzYpCmdsYW5jZShtb2RlbF82KQpgYGAKCmBgYHtyfQpwcmludChwYXN0ZSgnRGlzbWluaWNpb24gZGUgYWRqLnIuc3F1YXJlZDonLCBhYnMoMC4zNTIxMTMgLSAwLjI3MzQ4MjEpICogMTAwLCAnJScpKQpgYGAKCkRhZGEgbGEgcHJlc2VuY2lhIGRlIG91dGxpZXJzIGVuIGxhIHZhcmlhYmxlIHBlc28sIGVsICRSXjIkIEFqdXN0YWRvIGJhamEgY29uIHJlc3BlY3RvIGFsICoqbW9kZWxvCjEqKi4KCmBgYHtyfQptb2RlbHMgPC0gbGlzdCgnTW9kZWxvIDYnPW1vZGVsXzYpCgptb2RlbHNfZXZhbHVhdGlvbl9zdW1tYXJ5KG1vZGVscywgdHJhaW5fc2V0LCBtZXRyaWNfZm4gPSBybXNlKQptb2RlbHNfZXZhbHVhdGlvbl9zdW1tYXJ5KG1vZGVscywgdHJhaW5fc2V0LCBtZXRyaWNfZm4gPSBtYWUpCmBgYAoKUG9yIG90cm8gbGFkbywgYXVtZW50byBlbCBlcnJvciBkZSBwcmVkaWNjacOzbiB0YW50byBlbiB0cmFpbiBjb21vIGVuIHRlc3QuIEZpbmFsbWVudGUsIGVsIG1vZGVsbwp0aWVuZSB1biBncmFkbyBkZSBvdmVyZml0dGluZyBtdWNobyBtYXlvciBxdWUgbG9zIG1vZGVsb3MgYW50ZXJpb3JlcywgeWEgcXVlIGxhIG3DqXRyaWNhIGRlCmV2YWx1YWNpw7NuIGVuIHRlc3QgeSB0cmFpbiB0aWVuZSB1bmEgZGlmZXJlbmNpYSBtdXkgcHJvbnVuY2lhZGEgZGUgMS43IHB1bnRvcy4KCioqTW9kZWxvIDcqKgoKRGVmaW5pbW9zIHVuIG1vZGVsbyBpZ3VhbCBhbCAqKm1vZGVsbyAxKiogZW50cmVuYW5kbyBlbiBlbCBkYXRhc2V0IGRlIGVudHJlbmFtaWVudG8gb3JpZ2luYWwgeQp1c2Ftb3MgdW4gbW9kZWxvIGxpbmVhbCByb2J1c3RvLgoKYGBge3J9Cm1vZGVsXzcgPC0gcmxtKAogIHBlc28gfiBhbHR1cmEgKyBlZGFkICsgZGlhc19hY3RpdmlkYWRfZmlzaWNhX3NlbWFuYWwgKyBjb25zdW1vX2RpYXJpb19hbGNvaG9sLCAKICBkYXRhID0gbmV3X3RyYWluX3NldAopCmNvZWZmaWNpZW50c19zdW1tYXJ5KG1vZGVsXzcpCmBgYAoKYGBge3J9CmFub3ZhX3N1bW1hcnkobW9kZWxfNykKYGBgCgpgYGB7cn0KbW9kZWxzIDwtIGxpc3QoJ01vZGVsbyA2Jz1tb2RlbF82LCAnTW9kZWxvIDcnPW1vZGVsXzcpCgptb2RlbHNfZXZhbHVhdGlvbl9zdW1tYXJ5KG1vZGVscywgdGVzdF9zZXQsIG1ldHJpY19mbiA9IHJtc2UpCm1vZGVsc19ldmFsdWF0aW9uX3N1bW1hcnkobW9kZWxzLCB0ZXN0X3NldCwgbWV0cmljX2ZuID0gbWFlKQpgYGAKCkVsIG1vZGVsbyBsaW5lYWwgcm9idXN0byAoTW9kZWxvIDcpIHBhcmVjZSB0ZW5lciB1biBtZW5vciBlcnJvciBkZSBlbnRyZW5hbWllbnRvIG11eSBjZXJjYW5vIGFsCm1vZGVsbyA2LCBwZXJvIHRpZW5lIG1heW9yIHNvYnJlLSBhanVzdGUgcXVlIGVsIG1vZGVsbyA2LCBhdW5xdWUgZXMgdW5hIGRpZmVyZW5jaWEgbXV5IGJhamEuCgpEYWRvIGVzdG8sIHNlcmlhIHVuYSBidWVuYSBzZWxlY2Npb25vIGVsZWdpciBlbCBtb2RlbG8gNywgeWEgcXVlIGVsIHNvYnJlIGFqdXN0ZSBwcmFjdGljYW1lbnRlIG5vCmNhbWJpYSB5IG9idGVuZW1vcyB1biBlcnJvciBkZSBwcmVkaWNjacOzbiBlbiB0ZXN0IGxpZ2VyYW1lbnRlIG1lbm9yLgo=